ev3duder  0.3.0
EV3 Downloader/Uploader
tcp.c
Go to the documentation of this file.
1 
7 #ifdef __WIN32
8 
9 #define WIN32_LEAN_AND_MEAN
10 #include <windows.h>
11 #include <Ws2tcpip.h>
12 
13 #define bailout(msg) ({\
14  char *buf; \
15  int error = WSAGetLastError(); \
16  if (FormatMessageA( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,\
17  NULL, error, 0, (char*)&buf, 0, NULL) && error) \
18  fprintf(stderr, "%s. Error message: %s\n", (msg), buf);\
19  else \
20  fprintf(stderr, "%s. Error code: %d\n", (msg), error); \
21  LocalFree(buf);\
22  })
23 #define SHUT_RDWR SD_BOTH
24 
25 static inline const char * inet_ntop(int af, const void * restrict src, char * restrict dst, socklen_t size);
26 static inline int inet_pton(int af, const char *src, void *dst);
27 
28 typedef int socklen_t;
29 
30 #define socksetblock(sock, y_n) \
31  ioctlsocket((sock), FIONBIO, &(unsigned long){!(y_n)})
32 
33 #else /* POSIX */
34 
35 typedef int SOCKET;
36 #define INVALID_SOCKET (-1)
37 #define SOCKET_ERROR (-1)
38 #define bailout(msg) perror((msg))
39 #define closesocket close
40 #define socksetblock(sock, y_n) ((y_n) ? \
41  fcntl((sock), F_SETFL, fcntl((sock), F_GETFL, 0) & ~O_NONBLOCK) : \
42  fcntl((sock), F_SETFL, O_NONBLOCK))
43 
44 #include <unistd.h>
45 #include <sys/types.h>
46 #include <sys/socket.h>
47 #include <netinet/in.h>
48 #include <arpa/inet.h>
49 #include <sys/time.h>
50 #include <netdb.h>
51 #include <fcntl.h>
52 #include <errno.h>
53 #include "btserial.h"
54 
55 #endif
56 #include <assert.h>
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include "defs.h"
61 #include "tcp.h"
62 
64 #define UDP_PORT 3015
65 #define TCP_PORT 5555
66 
67 #define UDP_RECV_TIMEOUT 6
68 #define TCP_CONNECT_TIMEOUT 1
69 
84 void *tcp_open(const char *serial, unsigned timeout)
85 {
86 #ifdef _WIN32
87  DWORD tv_udp = 1000*(timeout ?: UDP_RECV_TIMEOUT);
88  WSADATA wsa;
89  if (WSAStartup(MAKEWORD(2,2),&wsa) != 0)
90  {
91  fprintf(stderr, "Failed to initialize Winsock2. Error Code : %d\n", WSAGetLastError());
92  return NULL;
93  }
94 #else
95  struct timeval tv_udp = {.tv_sec = timeout ?: UDP_RECV_TIMEOUT};
96 #endif
97 
98  struct tcp_handle *fdp = malloc(sizeof *fdp);
99  SOCKET fd;
100  char buffer[128];
101  struct sockaddr_in servaddr;
102  memset(&servaddr, 0, sizeof servaddr);
103  ssize_t n;
104 
105  if (serial && strchr(serial, '.')) // we have got an ip
106  {
107  /*
108  * This only works because the LEGO firmware doesn't even parse the
109  * submitted serialnumber. It just strstr(2)s for /target?sn and reports
110  * success if its found. This isn't guaranteed to stay, however
111  */
112  servaddr.sin_family = AF_INET;
113  switch(inet_pton(AF_INET, serial, &servaddr.sin_addr)){
114  case 1: break;
115  case 0: fprintf(stderr, "Invalid IP specified '%s'\n", serial); return NULL;
116  case -1: bailout("Parsing IP failed"); return NULL;
117  }
118  strncpy(fdp->name, "[n/a]", sizeof fdp->name);
119  strncpy(fdp->protocol, "[n/a]", sizeof fdp->protocol);
120  strncpy(fdp->serial, "[n/a]", sizeof fdp->serial);
121  servaddr.sin_port = htons(fdp->tcp_port = TCP_PORT);
122  strncpy(fdp->ip, serial, sizeof fdp->ip);
123 
124  }else{ // we need to search for it
125  if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET)
126  {
127  bailout("Failed to create socket");
128  return NULL;
129  }
130 
131  servaddr.sin_family = AF_INET;
132  servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
133  servaddr.sin_port = htons(UDP_PORT);
134  if (bind(fd, (struct sockaddr *)&servaddr, sizeof servaddr) == SOCKET_ERROR)
135  {
136  closesocket(fd);
137  bailout("Failed to bind");
138  return NULL;
139  }
140 
141  struct sockaddr_in cliaddr;
142  socklen_t len = sizeof cliaddr;
143  do {
144  setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (void*)&tv_udp, sizeof tv_udp);
145  n = recvfrom(fd, buffer,sizeof buffer,0,(struct sockaddr *)&cliaddr,&len);
146  if (n == SOCKET_ERROR)
147  {
148  closesocket(fd);
149  bailout("Failed to recieve broadcast"); // normally means timeout
150  return NULL;
151  }
152 
153  buffer[n] = '\0';
154  sscanf(buffer,
155  "Serial-Number: %s\r\n"
156  "Port: %u\r\n"
157  "Name: %s\r\n"
158  "Protocol: %s\r\n",
159  fdp->serial, &fdp->tcp_port, fdp->name, fdp->protocol);
160  }while(serial && strcmp(serial, fdp->serial) != 0);
161 
162  n = sendto(fd, (char[]){0x00}, 1, 0, (struct sockaddr *)&cliaddr, sizeof cliaddr);
163  closesocket(fd);
164  if (n == SOCKET_ERROR)
165  {
166  bailout("Failed to initiate handshake");
167  return NULL;
168  }
169 
170  memset(&servaddr, 0, sizeof servaddr);
171  // FIXME: use designated initializers, use getpeername, use C99 (gustedt)
172  // TODO: couldn't I use cliaddr directly?!
173  servaddr.sin_family = AF_INET;
174  servaddr.sin_addr.s_addr = cliaddr.sin_addr.s_addr;
175  inet_ntop(AF_INET, &cliaddr.sin_addr, fdp->ip, sizeof fdp->ip);
176  servaddr.sin_port = htons(fdp->tcp_port);
177  }
178 
179  fd = socket(AF_INET, SOCK_STREAM, 0);
180 
181  socksetblock(fd, 0);
182 
183  n = connect(fd, (struct sockaddr *)&servaddr, sizeof servaddr);
184  fd_set fdset;
185  FD_ZERO(&fdset);
186  FD_SET(fd, &fdset);
187  struct timeval tv_tcp = {.tv_sec = timeout ?: TCP_CONNECT_TIMEOUT};
188  if (select(fd + 1, NULL, &fdset, NULL, &tv_tcp) == 1)
189  {
190 #ifndef _WIN32
191  int so_error;
192  socklen_t len = sizeof so_error;
193  getsockopt(fd, SOL_SOCKET, SO_ERROR, &so_error, &len);
194  if (so_error == 0)
195 #endif
196  n = 0;
197  }
198  socksetblock(fd, 1);
199 
200  if (n == SOCKET_ERROR)
201  {
202  closesocket(fd);
203  bailout("Failed to initiate TCP connection");
204  return NULL;
205  }
206 
207  n = snprintf(buffer, sizeof buffer,
208  "GET /target?sn=%s VMTP1.0\nProtocol: %s",
209  fdp->serial, fdp->protocol);
210  n = sendto(fd, buffer, n, 0, (struct sockaddr *)&servaddr, sizeof servaddr);
211  if (n == SOCKET_ERROR)
212  {
213  closesocket(fd);
214  bailout("Failed to handshake over TCP");
215  return NULL;
216  }
217 
218  n=recvfrom(fd, buffer, sizeof buffer, 0, NULL, NULL);
219  if (n == SOCKET_ERROR)
220  {
221  closesocket(fd);
222  bailout("Failed to recieve TCP-connection confirmation");
223  return NULL;
224  }
225 
226  buffer[n] = '\0';
227  // printf("buffer=%s\n", buffer);
228 #ifdef _WIN32
229  fdp->sock = (LPVOID)fd;
230 #else
231  fdp->fd[0] = fdp->fd[1] = fd;
232 #endif
233  return fdp;
234 }
235 
236 
241 void tcp_close(void *sock)
242 {
243  shutdown(*(SOCKET*)sock, SHUT_RDWR);
244  closesocket(*(SOCKET*)sock);
245  free(sock);
246 #ifdef _WIN32
247  WSACleanup();
248 #endif
249 }
256 const wchar_t *tcp_error(void* fd_) { (void)fd_; return L"Errors not implemented yet";}
257 
258 #ifdef _WIN32
259 int tcp_write(void* sock, const u8* buf, size_t count) {
260  buf++;count--;
261  return send(*(SOCKET*)sock, (char*)buf, count, 0);
262 }
263 int tcp_read(void* sock, u8* buf, size_t count, int milliseconds) {
264  //FIXME: Timeout!
265  (void) milliseconds;
266  buf++;count--;
267  return recv(*(SOCKET*)sock, (char*)buf, count, 0);
268 }
269 #else
270 
271 int (*tcp_write)(void* device, const u8* buf, size_t count) = bt_write;
272 int (*tcp_read)(void* device, u8* buf, size_t count, int milliseconds) = bt_read;
273 
274 #endif
275 
276 #ifdef _WIN32
277 static inline const char *inet_ntop(int af, const void * restrict src, char * restrict dst, socklen_t size)
278 {
279  assert(af == AF_INET);
280  union { u32 addr; u8 sub[4];} ip = {((struct in_addr*)src)->s_addr};
281  snprintf(dst, size, "%u.%u.%u.%u",
282  ip.sub[0], ip.sub[1], ip.sub[2], ip.sub[3]);
283  return dst;
284 }
285 static inline int inet_pton(int af, const char *src, void *dst)
286 {
287  assert(af == AF_INET);
288  union { u32 addr; u8 sub[4];} ip;
289  int n =
290  sscanf(src, "%hhu.%hhu.%hhu.%hhu",
291  &ip.sub[0], &ip.sub[1], &ip.sub[2], &ip.sub[3]);
292  if (n != 4)
293  return 0;
294 
295  ((struct in_addr*)dst)->s_addr = ip.addr;
296  return 1;
297 }
298 
299 #endif
300 
unsigned tcp_port
Definition: tcp.h:19
#define INVALID_SOCKET
Definition: tcp.c:36
#define UDP_PORT
default UDP broadcast port
Definition: tcp.c:64
unsigned serial
Definition: main.c:118
#define TCP_CONNECT_TIMEOUT
Definition: tcp.c:68
char name[96]
Definition: tcp.h:20
#define socksetblock(sock, y_n)
Definition: tcp.c:40
#define SOCKET_ERROR
Definition: tcp.c:37
int bt_write(void *fd_, const u8 *buf, size_t count)
writes buf[1] till buf[count - 2] to device
Definition: bt-unix.c:51
int send()
Definition: send.c:6
int fd[2]
Definition: tcp.h:15
unsigned select
Definition: main.c:116
int bt_read(void *fd_, u8 *buf, size_t count, int milliseconds)
writes buf[1] till buf[count - 2] to device
Definition: bt-unix.c:77
char ip[48]
Definition: tcp.h:18
#define assert(cond)
Definition: main.c:17
uint32_t u32
Definition: defs.h:11
Input/Output wrappers for Bluetooth.
unsigned timeout
Definition: main.c:126
void * tcp_open(const char *serial, unsigned timeout)
connects to a ev3 device on the same subnet.
Definition: tcp.c:84
#define UDP_RECV_TIMEOUT
Definition: tcp.c:67
int SOCKET
BSD Sockets/Winsock2 I/O wrappers.
Definition: tcp.c:35
#define bailout(msg)
Definition: tcp.c:38
#define closesocket
Definition: tcp.c:39
#define TCP_PORT
Definition: tcp.c:65
int(* tcp_write)(void *device, const u8 *buf, size_t count)
writes buf[1] till buf[count - 2] to device
Definition: tcp.c:271
uint8_t u8
Definition: defs.h:9
char protocol[8]
Definition: tcp.h:21
char serial[15]
Definition: tcp.h:17
void tcp_close(void *sock)
releases the socket file descriptor opened by tcp_open()
Definition: tcp.c:241
BSD sockets/Winsock2 Input/Output wrappers.
const wchar_t * tcp_error(void *fd_)
Returns an error string describing the last error occured.
Definition: tcp.c:256
Definition: tcp.h:11
int(* tcp_read)(void *device, u8 *buf, size_t count, int milliseconds)
writes buf[1] till buf[count - 2] to device
Definition: tcp.c:272