ytcp.c
Go to the documentation of this file.
1 /*********************************************************************
2  *
3  * $Id: ytcp.c 28224 2017-07-31 14:53:54Z seb $
4  *
5  * Implementation of a client TCP stack
6  *
7  * - - - - - - - - - License information: - - - - - - - - -
8  *
9  * Copyright (C) 2011 and beyond by Yoctopuce Sarl, Switzerland.
10  *
11  * Yoctopuce Sarl (hereafter Licensor) grants to you a perpetual
12  * non-exclusive license to use, modify, copy and integrate this
13  * file into your software for the sole purpose of interfacing
14  * with Yoctopuce products.
15  *
16  * You may reproduce and distribute copies of this file in
17  * source or object form, as long as the sole purpose of this
18  * code is to interface with Yoctopuce products. You must retain
19  * this notice in the distributed source file.
20  *
21  * You should refer to Yoctopuce General Terms and Conditions
22  * for additional information regarding your rights and
23  * obligations.
24  *
25  * THE SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS" WITHOUT
26  * WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
27  * WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY, FITNESS
28  * FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO
29  * EVENT SHALL LICENSOR BE LIABLE FOR ANY INCIDENTAL, SPECIAL,
30  * INDIRECT OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA,
31  * COST OF PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY OR
32  * SERVICES, ANY CLAIMS BY THIRD PARTIES (INCLUDING BUT NOT
33  * LIMITED TO ANY DEFENSE THEREOF), ANY CLAIMS FOR INDEMNITY OR
34  * CONTRIBUTION, OR OTHER SIMILAR COSTS, WHETHER ASSERTED ON THE
35  * BASIS OF CONTRACT, TORT (INCLUDING NEGLIGENCE), BREACH OF
36  * WARRANTY, OR OTHERWISE.
37  *
38  *********************************************************************/
39 
40 #define __FILE_ID__ "ytcp"
41 #define _WINSOCK_DEPRECATED_NO_WARNINGS
42 
43 #include "ydef.h"
44 #if defined(WINDOWS_API) && !defined(_MSC_VER)
45 #define _WIN32_WINNT 0x501
46 #endif
47 #ifdef WINDOWS_API
48 typedef int socklen_t;
49 #if defined(__BORLANDC__)
50 #pragma warn -8004
51 #pragma warn -8019
52 #include <winsock2.h>
53 #include <ws2tcpip.h>
54 #pragma warn +8004
55 #pragma warn +8019
56 #else
57 #include <winsock2.h>
58 #include <ws2tcpip.h>
59 #endif
60 #endif
61 #include "ytcp.h"
62 #include "yproto.h"
63 #include "yhash.h"
64 
65 #ifdef WIN32
66  #ifndef WINCE
67  #include <iphlpapi.h>
68  #if defined(_MSC_VER) || defined (__BORLANDC__)
69  #pragma comment(lib, "Ws2_32.lib")
70  #endif
71  #else
72  #pragma comment(lib, "Ws2.lib")
73  #endif
74 #else
75  #include <unistd.h>
76  #include <fcntl.h>
77  #include <netdb.h>
78 #endif
79 
80 
81 
82 //#define DEBUG_SLOW_TCP
83 //#define TRACE_TCP_REQ
84 //#define PERF_TCP_FUNCTIONS
85 #ifdef PERF_TCP_FUNCTIONS
86 
87 
88 typedef struct {
89  yPerfMon TCPOpen_socket;
90  yPerfMon TCPOpen_connect;
91  yPerfMon TCPOpen_setsockopt_noblock;
92  yPerfMon TCPOpen_setsockopt_nodelay;
93  yPerfMon TCPOpenReq_wait;
94  yPerfMon TCPOpenReq;
95  yPerfMon tmp1;
96  yPerfMon tmp2;
97  yPerfMon tmp3;
98  yPerfMon tmp4;
99 } yTcpPerfMonSt;
100 
101 yTcpPerfMonSt yTcpPerf;
102 
103 
104 #define YPERF_TCP_ENTER(NAME) {yTcpPerf.NAME.count++;yTcpPerf.NAME.tmp=yapiGetTickCount();}
105 #define YPERF_TCP_LEAVE(NAME) {yTcpPerf.NAME.leave++;yTcpPerf.NAME.totaltime += yapiGetTickCount()- yTcpPerf.NAME.tmp;}
106 
107 
108 void dumpYTcpPerf(void)
109 {
110  dumpYPerfEntry(&yTcpPerf.TCPOpen_socket,"TCPOpen:socket");
111  dumpYPerfEntry(&yTcpPerf.TCPOpen_connect,"TCPOpen:connect");
112  dumpYPerfEntry(&yTcpPerf.TCPOpen_setsockopt_noblock,"TCPOpen:sockopt_noblock");
113  dumpYPerfEntry(&yTcpPerf.TCPOpen_setsockopt_nodelay,"TCPOpen:sockopt_nodelay");
114  dumpYPerfEntry(&yTcpPerf.TCPOpenReq_wait,"TCPOpenReq:wait");
115  dumpYPerfEntry(&yTcpPerf.TCPOpenReq,"TCPOpenReq");
116  dumpYPerfEntry(&yTcpPerf.tmp1,"TCP:tmp1");
117  dumpYPerfEntry(&yTcpPerf.tmp2,"TCP:tmp2");
118  dumpYPerfEntry(&yTcpPerf.tmp3,"TCP:tmp3");
119  dumpYPerfEntry(&yTcpPerf.tmp4,"TCP:tmp4");
120 }
121 #else
122 #define YPERF_TCP_ENTER(NAME)
123 #define YPERF_TCP_LEAVE(NAME)
124 #endif
125 
126 
127 
128 void yDupSet(char **storage, const char *val)
129 {
130  int len = (val ? (int)strlen(val)+1 : 1);
131 
132  if(*storage) yFree(*storage);
133  *storage = (char*) yMalloc(len);
134  if(val) {
135  memcpy(*storage, val, len);
136  } else {
137  **storage = 0;
138  }
139 }
140 
141 int yNetSetErrEx(u32 line,unsigned err,char *errmsg)
142 {
143  int len;
144  if(errmsg==NULL)
145  return YAPI_IO_ERROR;
146  YSPRINTF(errmsg,YOCTO_ERRMSG_LEN,"%s:%d:tcp(%d):",__FILE_ID__,line,err);
147  dbglog("yNetSetErrEx -> %s:%d:tcp(%d)\n",__FILE_ID__,line,err);
148 
149 #if defined(WINDOWS_API) && !defined(WINCE)
150  len=(int)strlen(errmsg);
151  FormatMessageA (
152  FORMAT_MESSAGE_FROM_SYSTEM |
153  FORMAT_MESSAGE_IGNORE_INSERTS,
154  NULL,
155  err,
156  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
157  (LPSTR) (errmsg+len),
158  YOCTO_ERRMSG_LEN-len, NULL );
159 #else
160  len=YSTRLEN(errmsg);
161  strcpy(errmsg+len, strerror((int)err));
162 #endif
163  return YAPI_IO_ERROR;
164 }
165 #if 1
166 #define yNetLogErr() yNetLogErrEx(__LINE__,SOCK_ERR)
167 static int yNetLogErrEx(u32 line,unsigned err)
168 {
169  int retval;
170  char errmsg[YOCTO_ERRMSG_LEN];
171  retval = yNetSetErrEx(line,err,errmsg);
172  dbglog("%s",errmsg);
173  return retval;
174 }
175 #endif
176 
177 #ifdef DEBUG_SOCKET_USAGE
178 
179 #define yclosesocket(skt) yclosesocket_ex(__FILE_ID__, __LINE__, skt)
180 void yclosesocket_ex(const char *file, int line, YSOCKET skt)
181 {
182  dbglogf(file, line, "close socket %x\n", skt);
183  closesocket(skt);
184 }
185 
186 
187 #define ysocket(domain, type, protocol) ysocket_ex(__FILE_ID__, __LINE__, domain, type, protocol)
188 YSOCKET ysocket_ex(const char *file, int line, int domain, int type, int protocol)
189 {
190  YSOCKET skt = socket(domain, type, protocol);
191  dbglogf(file, line, "open socket %x (%x,%x,%x)\n", skt, domain, type, protocol);
192  return skt;
193 }
194 
195 #define ysend(skt, buf, len, flags) ysend_ex(__FILE_ID__, __LINE__, skt, buf, len, flags)
196 int ysend_ex(const char * file, int line, YSOCKET skt, const char* buffer, int tosend, int flags)
197 {
198  int res = (int)send(skt, buffer, tosend, flags);
199  //dbglogf(file, line, "send socket %x (%d,%x -> %d)\n", skt, tosend, flags, res);
200  return res;
201 }
202 
203 #define yrecv(skt, buf, len, flags) yrecv_ex(__FILE_ID__, __LINE__, skt, buf, len, flags)
204 int yrecv_ex(const char * file, int line, YSOCKET skt, char *buf, int len, int flags)
205 {
206  int res = recv(skt, buf, len, flags);
207  //dbglogf(file, line, "read socket %x (%d,%x -> %d)\n", skt, len, flags, res);
208  return res;
209 }
210 
211 #else
212 #define yclosesocket(skt) closesocket(skt)
213 #define ysocket(domain, type, protocol) socket(domain, type, protocol)
214 #define ysend(skt, buf, len, flags) send(skt, buf, len, flags)
215 #define yrecv(skt, buf, len, flags) recv(skt, buf, len, flags)
216 #endif
217 
219 {
220  wuce->listensock = INVALID_SOCKET;
221  wuce->signalsock = INVALID_SOCKET;
222 }
223 
224 
225 int yStartWakeUpSocket(WakeUpSocket *wuce, char *errmsg)
226 {
227  u32 optval;
228  socklen_t localh_size;
229  struct sockaddr_in localh;
230 
231  if(wuce->listensock != INVALID_SOCKET || wuce->signalsock != INVALID_SOCKET) {
232  return YERRMSG(YAPI_INVALID_ARGUMENT,"WakeUpSocket allready Started");
233  }
234  //create socket
235  wuce->listensock = ysocket(PF_INET,SOCK_DGRAM,IPPROTO_UDP);
236  if (wuce->listensock == INVALID_SOCKET) {
237  return yNetSetErr();
238  }
239  optval = 1;
240  setsockopt(wuce->listensock,SOL_SOCKET,SO_REUSEADDR,(char *)&optval,sizeof(optval));
241 
242  localh_size=sizeof(localh);
243  // set port to 0 since we accept any port
244  memset(&localh,0,localh_size);
245  localh.sin_family = AF_INET;
246  localh.sin_addr.s_addr = inet_addr("127.0.0.1");
247  if (bind(wuce->listensock,(struct sockaddr *)&localh,localh_size)<0) {
248  return yNetSetErr();
249  }
250  if (getsockname(wuce->listensock,(struct sockaddr *)&localh,&localh_size)<0) {
251  return yNetSetErr();
252  }
253  wuce->signalsock = ysocket(PF_INET,SOCK_DGRAM,IPPROTO_UDP);
254  if (wuce->signalsock == INVALID_SOCKET) {
255  return yNetSetErr();
256  }
257  if (connect(wuce->signalsock,(struct sockaddr *)&localh,localh_size)<0) {
258  return yNetSetErr();
259  }
260  return YAPI_SUCCESS;
261 }
262 
263 int yDringWakeUpSocket(WakeUpSocket *wuce, u8 signal, char *errmsg)
264 {
265  if(ysend(wuce->signalsock,(char*)&signal,1,SEND_NOSIGPIPE) < 0) {
266  return yNetSetErr();
267  }
268  return YAPI_SUCCESS;
269 }
270 
271 int yConsumeWakeUpSocket(WakeUpSocket *wuce, char *errmsg)
272 {
273  u8 signal = 0;
274 
275  if(yrecv(wuce->listensock,(char*)&signal,1,0) < 0) {
276  return yNetSetErr();
277  }
278  return signal;
279 }
280 
282 {
283  if ( wuce->listensock != INVALID_SOCKET) {
284  yclosesocket(wuce->listensock);
285  wuce->listensock = INVALID_SOCKET;
286  }
287  if ( wuce->signalsock != INVALID_SOCKET) {
288  yclosesocket(wuce->signalsock);
289  wuce->signalsock = INVALID_SOCKET;
290  }
291 }
292 
293 
294 
295 
296 u32 yResolveDNS(const char *name,char *errmsg)
297 {
298  u32 ipv4=0;
299 
300  struct addrinfo *infos,*p;
301  if(getaddrinfo(name,NULL,NULL,&infos)!=0){
302  REPORT_ERR("Unable to resolve hostname");
303  return 0;
304  }
305 
306  // Retrieve each address and print out the hex bytes
307  for(p=infos; p != NULL ; p=p->ai_next) {
308  if (p->ai_family == AF_INET){
309  ipv4 = ((struct sockaddr_in *) p->ai_addr)->sin_addr.s_addr;
310  break;
311  }
312  }
313  freeaddrinfo(infos);
314  return ipv4;
315 }
316 
317 
318 #define YDNS_CACHE_SIZE 16
319 #define YDNS_CACHE_VALIDITY 600000u //10 minutes
320 typedef struct {
322  u32 ip;
323  u64 time;
324 } DnsCache;
325 
327 
328 
329 static u32 resolveDNSCache(yUrlRef url, char *errmsg)
330 {
331  int i, firstFree = -1;
332  char buffer[YOCTO_HOSTNAME_NAME];
333  u32 ip;
334 
335  for (i = 0; i<YDNS_CACHE_SIZE; i++) {
336  if (dnsCache[i].url == url) {
337  break;
338  }
339  if (firstFree <0 && dnsCache[i].url == INVALID_HASH_IDX) {
340  firstFree = i;
341  }
342  }
343  if (i< YDNS_CACHE_SIZE) {
344  if ((u64)(yapiGetTickCount() - dnsCache[i].time) <= YDNS_CACHE_VALIDITY) {
345  return dnsCache[i].ip;
346  }
347  firstFree = i;
348  }
349  yHashGetUrlPort(url, buffer, NULL, NULL, NULL, NULL);
350  ip = yResolveDNS(buffer, errmsg);
351  if (ip != 0 && firstFree < YDNS_CACHE_SIZE) {
352  dnsCache[firstFree].url = url;
353  dnsCache[firstFree].ip = ip;
354  dnsCache[firstFree].time = yapiGetTickCount();
355  }
356  return ip;
357 }
358 
359 
360 /********************************************************************************
361 * Pure TCP funtions
362 *******************************************************************************/
363 
364 int yTcpInit(char *errmsg)
365 {
366  int i;
367 #ifdef WINDOWS_API
368  // Initialize Winsock 2.2
369  WSADATA wsaData;
370  int iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
371  if (iResult != 0){
372  return YERRMSG(YAPI_IO_ERROR,"Unable to start Windows Socket");
373  }
374 #endif
375  TCPLOG("yTcpInit\n");
376  for (i=0; i<YDNS_CACHE_SIZE;i++){
377  dnsCache[i].url = INVALID_HASH_IDX;
378  }
379  return YAPI_SUCCESS;
380 }
381 
382 void yTcpShutdown(void)
383 {
384 
385  TCPLOG("yTcpShutdown\n");
386 #ifdef PERF_TCP_FUNCTIONS
387  dumpYTcpPerf();
388 #endif
389 #ifdef WINDOWS_API
390  WSACleanup();
391 #endif
392 }
393 
394 
395 #define DEFAULT_TCP_ROUND_TRIP_TIME 30
396 #define DEFAULT_TCP_MAX_WINDOW_SIZE (4*65536)
397 
398 static int yTcpOpen(YSOCKET *newskt, u32 ip, u16 port, u64 mstimeout, char *errmsg)
399 {
400  struct sockaddr_in clientService;
401  int iResult;
402  u_long flags;
403  YSOCKET skt;
404  fd_set readfds, writefds, exceptfds;
405  struct timeval timeout;
406  int tcp_sendbuffer;
407 #ifdef WINDOWS_API
408  char noDelay=1;
409  int optlen;
410 #else
411  int noDelay=1;
412  socklen_t optlen;
413 #ifdef SO_NOSIGPIPE
414  int noSigpipe=1;
415 #endif
416 #endif
417 
418  TCPLOG("yTcpOpen %p [dst=%x:%d %dms]\n", newskt, ip, port, mstimeout);
419 
420  YPERF_TCP_ENTER(TCPOpen_socket);
421  *newskt = INVALID_SOCKET;
422  skt = ysocket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
423  YPERF_TCP_LEAVE(TCPOpen_socket);
424  if (skt == INVALID_SOCKET) {
425  REPORT_ERR("Error at socket()");
426  return YAPI_IO_ERROR;
427  }
428  //dbglog("ytcpOpen %X:%x: skt= %x\n",ip,port,skt);
429  YPERF_TCP_ENTER(TCPOpen_connect);
430  memset(&clientService, 0, sizeof(clientService));
431  //----------------------
432  // The sockaddr_in structure specifies the address family,
433  // IP address, and port of the server to be connected to.
434  clientService.sin_family = AF_INET;
435  clientService.sin_addr.s_addr = ip;
436  clientService.sin_port = htons( port );
437 
438  //----------------------
439  // Connect to server.
440  YPERF_TCP_ENTER(TCPOpen_setsockopt_noblock);
441  //set socket as non blocking
442 #ifdef WINDOWS_API
443  flags = 1;
444  ioctlsocket(skt, FIONBIO, &flags);
445 #else
446  flags = fcntl(skt, F_GETFL, 0);
447  fcntl(skt, F_SETFL, flags | O_NONBLOCK);
448 #ifdef SO_NOSIGPIPE
449  setsockopt(skt, SOL_SOCKET, SO_NOSIGPIPE, (void *)&noSigpipe, sizeof(int));
450 #endif
451 #endif
452  YPERF_TCP_LEAVE(TCPOpen_setsockopt_noblock);
453  connect(skt, ( struct sockaddr *) &clientService, sizeof(clientService) );
454 
455  // wait for the connection with a select
456  memset(&timeout, 0, sizeof(timeout));
457  if (mstimeout != 0) {
458  u64 nbsec = mstimeout / 1000;
459  timeout.tv_sec = (long)nbsec;
460  timeout.tv_usec = ((int) (mstimeout - (nbsec * 1000))) * 1000;
461  } else {
462  timeout.tv_sec = 20;
463  }
464  FD_ZERO(&readfds);
465  FD_ZERO(&writefds);
466  FD_ZERO(&exceptfds);
467  FD_SET(skt, &readfds);
468  FD_SET(skt, &writefds);
469  FD_SET(skt, &exceptfds);
470  iResult = select((int)skt + 1, &readfds, &writefds, &exceptfds, &timeout);
471  if (iResult < 0) {
472  REPORT_ERR("Unable to connect to server");
473  yclosesocket(skt);
474  return YAPI_IO_ERROR;
475  }
476  if (FD_ISSET(skt, &exceptfds)) {
477  yclosesocket(skt);
478  return YERRMSG(YAPI_IO_ERROR, "Unable to connect to server");
479  }
480  if (!FD_ISSET(skt, &writefds)) {
481  yclosesocket(skt);
482  return YERRMSG(YAPI_IO_ERROR, "Unable to connect to server");
483  }
484  YPERF_TCP_LEAVE(TCPOpen_connect);
485  if ( iResult == SOCKET_ERROR) {
486  REPORT_ERR("Unable to connect to server");
487  yclosesocket(skt);
488  return YAPI_IO_ERROR;
489  }
490  YPERF_TCP_ENTER(TCPOpen_setsockopt_nodelay);
491  if(setsockopt(skt, IPPROTO_TCP, TCP_NODELAY, &noDelay, sizeof(noDelay)) < 0) {
492 #if 0
493  switch(errno) {
494  case EBADF:
495  dbglog("The argument sockfd is not a valid descriptor.\n");
496  break;
497  case EFAULT:
498  dbglog("The address pointed to by optval is not in a valid part of the process address space. For getsockopt(), "
499  "this error may also be returned if optlen is not in a valid part of the process address space.\n");
500  break;
501  case EINVAL:
502  dbglog("optlen invalid in setsockopt(). In some cases this error can also occur for an invalid value in optval "
503  "(e.g., for the IP_ADD_MEMBERSHIP option described in ip(7)).\n");
504  break;
505  case ENOPROTOOPT:
506  dbglog("The option is unknown at the level indicated.\n");
507  break;
508  case ENOTSOCK:
509  dbglog("The argument sockfd is a file, not a socket.\n");
510  break;
511  }
512 #endif
513  dbglog("SetSockOpt TCP_NODELAY failed %d\n",errno);
514  }
515  YPERF_TCP_LEAVE(TCPOpen_setsockopt_nodelay);
516 
517  // Get buffer size
518  optlen = sizeof(tcp_sendbuffer);
519  if (getsockopt(skt, SOL_SOCKET, SO_SNDBUF, (void*)&tcp_sendbuffer, &optlen) >= 0) {
520 #if 0
521  dbglog("Default windows size is %d\n", tcp_sendbuffer);
522 #endif
523  if (tcp_sendbuffer < DEFAULT_TCP_MAX_WINDOW_SIZE) {
524  // Set buffer size to 64k
525  tcp_sendbuffer = DEFAULT_TCP_MAX_WINDOW_SIZE;
526  if (setsockopt(skt, SOL_SOCKET, SO_SNDBUF, (void*)&tcp_sendbuffer, sizeof(tcp_sendbuffer)) < 0) {
527 #if 0
528  switch (errno) {
529  case EBADF:
530  dbglog("The argument sockfd is not a valid descriptor.\n");
531  break;
532  case EFAULT:
533  dbglog("The address pointed to by optval is not in a valid part of the process address space. For getsockopt(), "
534  "this error may also be returned if optlen is not in a valid part of the process address space.\n");
535  break;
536  case EINVAL:
537  dbglog("optlen invalid in setsockopt(). In some cases this error can also occur for an invalid value in optval "
538  "(e.g., for the IP_ADD_MEMBERSHIP option described in ip(7)).\n");
539  break;
540  case ENOPROTOOPT:
541  dbglog("The option is unknown at the level indicated.\n");
542  break;
543  case ENOTSOCK:
544  dbglog("The argument sockfd is a file, not a socket.\n");
545  break;
546  }
547 #endif
548  dbglog("SetSockOpt SO_SNDBUF %d failed %d\n", tcp_sendbuffer, errno);
549  }
550  }
551  } else {
552  dbglog("getsockopt: unable to get tcp buffer size\n");
553  }
554 
555  *newskt = skt;
556 
557  return YAPI_SUCCESS;
558 }
559 
560 static void yTcpClose(YSOCKET skt)
561 {
562  // cleanup
563  yclosesocket(skt);
564 }
565 
566 
567 // check it a socket is still valid and empty (ie: nothing to read and writable)
568 // return 1 if the socket is valid or a error code
569 static int yTcpCheckSocketStillValid(YSOCKET skt, char * errmsg)
570 {
571  int iResult, res;
572  fd_set readfds,writefds,exceptfds;
573  struct timeval timeout;
574 
575  // Send an initial buffer
576 #ifndef WINDOWS_API
577 retry:
578 #endif
579  memset(&timeout,0,sizeof(timeout));
580  FD_ZERO(&readfds);
581  FD_ZERO(&writefds);
582  FD_ZERO(&exceptfds);
583  FD_SET(skt,&readfds);
584  FD_SET(skt,&writefds);
585  FD_SET(skt,&exceptfds);
586  res = select((int)skt+1,&readfds,&writefds,&exceptfds,&timeout);
587  if (res<0) {
588 #ifndef WINDOWS_API
589  if(SOCK_ERR == EAGAIN){
590  goto retry;
591  } else
592 #endif
593  {
594  res = yNetSetErr();
595  yTcpClose(skt);
596  return res;
597  }
598  }
599  if (FD_ISSET(skt,&exceptfds)) {
600  yTcpClose(skt);
601  return YERRMSG(YAPI_IO_ERROR, "Exception on socket");
602  }
603  if (!FD_ISSET(skt,&writefds)) {
604  yTcpClose(skt);
605  return YERRMSG(YAPI_IO_ERROR, "Socket not ready for write");
606  }
607 
608  if (FD_ISSET(skt,&readfds)) {
609  char buffer[128];
610  iResult = (int)yrecv(skt, buffer, sizeof(buffer), 0);
611  if (iResult == 0) {
612  yTcpClose(skt);
613  return YERR(YAPI_NO_MORE_DATA);
614  } if ( iResult < 0 ){
615  yTcpClose(skt);
616  return YERR(YAPI_IO_ERROR);
617  } else {
618  yTcpClose(skt);
619  return YERR(YAPI_DOUBLE_ACCES);
620  }
621  }
622  return 1;
623 }
624 
625 
626 static int yTcpWrite(YSOCKET skt, const char *buffer, int len,char *errmsg)
627 {
628  int res;
629  int tosend = len;
630  const char * p = buffer;
631 
632  while (tosend>0) {
633  res = (int) ysend(skt, p, tosend, SEND_NOSIGPIPE);
634  if (res == SOCKET_ERROR) {
635 #ifdef WINDOWS_API
636  if(SOCK_ERR != WSAEWOULDBLOCK)
637 #else
638  if(SOCK_ERR != EAGAIN)
639 
640 #endif
641  {
642  return yNetSetErr();
643  }
644 
645  } else {
646  tosend -= res;
647  p += res;
648  // unable to send all data
649  // wait a bit with a select
650  if (tosend != res) {
651  struct timeval timeout;
652  fd_set fds;
653  memset(&timeout,0,sizeof(timeout));
654  // Upload of large files (external firmware updates) may need
655  // a long time to process (on OSX: seen more than 40 seconds !)
656  timeout.tv_sec = 60;
657  FD_ZERO(&fds);
658  FD_SET(skt,&fds);
659  res = select((int)skt+1,NULL,&fds,NULL,&timeout);
660  if (res<0) {
661 #ifndef WINDOWS_API
662  if(SOCK_ERR == EAGAIN){
663  continue;
664  } else
665 #endif
666  {
667  return yNetSetErr();
668  }
669  } else if (res == 0) {
670  return YERRMSG(YAPI_TIMEOUT, "Timeout during TCP write");
671  }
672  }
673  }
674  }
675  return len;
676 }
677 
678 
679 static int yTcpRead(YSOCKET skt, u8 *buffer, int len,char *errmsg)
680 {
681  int iResult = (int)yrecv(skt, (char*)buffer, len, 0);
682 
683  if (iResult == 0){
684  return YERR(YAPI_NO_MORE_DATA);
685  }else if ( iResult < 0 ){
686 #ifdef WINDOWS_API
687  if(SOCK_ERR == WSAEWOULDBLOCK){
688  return 0;
689  }
690 #else
691  if(SOCK_ERR == EAGAIN){
692  return 0;
693  }
694 #endif
695  REPORT_ERR("read failed");
696  return YAPI_IO_ERROR;
697  }
698  return iResult;
699 }
700 
701 
702 int yTcpDownload(const char *host, const char *url, u8 **out_buffer, u32 mstimeout, char *errmsg)
703 {
704  YSOCKET skt;
705  u32 ip;
706  int res,len,readed;
707  char request[512];
708  u8 *replybuf = yMalloc(512);
709  int replybufsize = 512;
710  int replysize = 0;
711  fd_set fds;
712  u64 expiration;
713 
714  ip = yResolveDNS(host, errmsg);
715  if (ip==0) {
716  yFree(replybuf);
717  return YAPI_IO_ERROR;
718  }
719  expiration = yapiGetTickCount() + mstimeout;
720  if (yTcpOpen(&skt, ip, 80, mstimeout, errmsg)<0) {
721  yTcpClose(skt);
722  yFree(replybuf);
723  return YAPI_IO_ERROR;
724  }
725  len = YSPRINTF(request,512,"GET %s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n"
726  "Accept-Encoding:\r\nUser-Agent: Yoctopuce\r\n\r\n",url,host);
727  //write header
728  res = yTcpWrite(skt, request, len, errmsg);
729  if(YISERR(res)) {
730  goto exit;
731  }
732  while(expiration - yapiGetTickCount() > 0) {
733  struct timeval timeout;
734  u64 ms = expiration - yapiGetTickCount();
735  memset(&timeout,0,sizeof(timeout));
736  timeout.tv_sec = (long) ms / 1000;
737  timeout.tv_usec = (int)(ms % 1000) *1000;
738  /* wait for data */
739  FD_ZERO(&fds);
740  FD_SET(skt,&fds);
741  res = select((int)skt+1,&fds,NULL,NULL,&timeout);
742  if (res<0) {
743  #ifndef WINDOWS_API
744  if(SOCK_ERR == EAGAIN){
745  continue;
746  } else
747  #endif
748  {
749  res = yNetSetErr();
750  goto exit;
751  }
752  }
753  if(replysize + 256 >= replybufsize) {
754  // need to grow receive buffer
755  int newsize = replybufsize << 1;
756  u8 *newbuf = (u8*) yMalloc(newsize);
757  if (replybuf) {
758  memcpy(newbuf, replybuf, replysize);
759  yFree(replybuf);
760  }
761  replybuf = newbuf;
762  replybufsize = newsize;
763  }
764  readed = yTcpRead(skt, replybuf + replysize, replybufsize - replysize, errmsg);
765  if(readed < 0) {
766  // any connection closed by peer ends up with YAPI_NO_MORE_DATA
767  if (readed == YAPI_NO_MORE_DATA) {
768  res = replysize;
769  } else {
770  res = readed;
771  }
772  goto exit;
773  } else {
774  replysize += readed;
775  }
776  }
777  res = YERR(YAPI_TIMEOUT);
778 
779 exit:
780  yTcpClose(skt);
781  if (res < 0) {
782  yFree(replybuf);
783  } else {
784  *out_buffer = replybuf;
785  }
786  return res;
787 }
788 
789 
790 
791 
792 static int yTcpCheckReqTimeout(struct _RequestSt *req, char *errmsg)
793 {
794  if (req->timeout_tm != 0) {
795  u64 now = yapiGetTickCount();
796  u64 duration = now - req->open_tm;
797  u64 last_io = (req->write_tm > req->read_tm ? req->write_tm : req->read_tm);
798  u64 idle_durration = now -last_io;
799 
800  if (idle_durration < YIO_IDLE_TCP_TIMEOUT) {
801  return YAPI_SUCCESS;
802  }
803 #ifdef DEBUG_SLOW_TCP
804  else {
805  u64 last_wr = now - req->write_tm;
806  u64 last_rd = now - req->read_tm;
807 
808  dbglog("Long Idle TCP request %p = %"FMTu64"ms total = %"FMTu64"ms (read=%"FMTu64"ms write=%"FMTu64")\n",
809  req, idle_durration, duration, last_rd, last_wr);
810  }
811 #endif
812  if (duration > req->timeout_tm) {
813  req->errcode = YAPI_TIMEOUT;
814  YSPRINTF(req->errmsg, YOCTO_ERRMSG_LEN , "TCP request took too long (%dms)",duration);
815  return YERRMSG(YAPI_TIMEOUT, req->errmsg);
816  }
817 #ifdef DEBUG_SLOW_TCP
818  else {
819  if (duration > (req->timeout_tm - (req->timeout_tm / 4))) {
820  dbglog("Slow TCP request %p = %dms\n",req,duration);
821  dbglog("req = %s\n",req->headerbuf);
822  }
823  }
824 #endif
825 
826  }
827  return YAPI_SUCCESS;
828 }
829 
830 
831 /********************************************************************************
832 * HTTP request funtions (http request that DO NOT use Websocket)
833 *******************************************************************************/
834 
835 
836 
837 // access mutex taken by caller
838 static int yHTTPOpenReqEx(struct _RequestSt *req, u64 mstimout, char *errmsg)
839 {
840  char buffer[YOCTO_HOSTNAME_NAME], *p,*last,*end;
841  u32 ip;
842  u16 port;
843  int res;
844 
845  YASSERT(req->proto == PROTO_AUTO || req->proto == PROTO_HTTP);
846 
847  switch (yHashGetUrlPort(req->hub->url, buffer, &port, NULL, NULL, NULL)) {
848  case NAME_URL:
849  ip = resolveDNSCache(req->hub->url, errmsg);
850  if (ip == 0) {
851  YPERF_TCP_LEAVE(tmp1);
852  return YAPI_IO_ERROR;
853  }
854  break;
855  case IP_URL:
856  ip = inet_addr(buffer);
857  break;
858  default:
859  res = YERRMSG(YAPI_IO_ERROR, "not an IP hub");
860  req->http.skt = INVALID_SOCKET;
861  TCPLOG("yTcpOpenReqEx error%p[%x]\n", req, req->http.skt);
862  return res;
863  }
864  TCPLOG("yTcpOpenReqEx %p [%x:%x %d]\n", req, req->http.skt, req->http.reuseskt, mstimout);
865 
866  req->replypos = -1; // not ready to consume until header found
867  req->replysize = 0;
868  memset(req->replybuf, 0, req->replybufsize);
869  req->errcode = YAPI_SUCCESS;
870 
871 
872  if (req->http.reuseskt != INVALID_SOCKET && (res = yTcpCheckSocketStillValid(req->http.reuseskt, NULL)) == 1) {
873  req->http.skt = req->http.reuseskt;
875  } else {
877  res = yTcpOpen(&req->http.skt, ip, port, mstimout, errmsg);
878  if (YISERR(res)) {
879  // yTcpOpen has reset the socket to INVALID
880  yTcpClose(req->http.skt);
881  req->http.skt = INVALID_SOCKET;
882  TCPLOG("yTcpOpenReqEx error %p [%x]\n", req, req->http.skt);
883  return res;
884  }
885  }
886 
887  p = req->headerbuf;
888  //skip first line
889  while(*p && *p != '\r') p++;
890  end=p;
891  last=p;
892 
893  while(*p == '\r' && *(p+1)=='\n' && *(p+2)!='\r') {
894  p+=2;
895  while(*p && *p != '\r') p++;
896  if (YSTRNCMP(last,"\r\nContent-Type",strlen("\r\nContent-Type"))==0){
897  unsigned len = (unsigned)(p - last);
898  if(last != end){
899  memcpy(end,last,len);
900  }
901  end += len;
902  }
903  last = p;
904  }
905  *end++ = '\r'; *end++ = '\n';
906  // insert authorization header in needed
908  if(req->hub->http.s_user && req->hub->http.s_realm) {
909  char *method = req->headerbuf, *uri;
910  char *auth = end;
911  // null-terminate method and uri for digest computation
912  // ReSharper disable once CppPossiblyErroneousEmptyStatements
913  for (uri = method; *uri != ' '; uri++);
914  *uri++ = 0;
915  // ReSharper disable once CppPossiblyErroneousEmptyStatements
916  for(p = uri; *p != ' '; p++);
917  *p = 0;
918  yDigestAuthorization(auth, (int)(req->headerbuf + req->headerbufsize - auth), req->hub->http.s_user, req->hub->http.s_realm, req->hub->http.s_ha1,
919  req->hub->http.s_nonce, req->hub->http.s_opaque, &req->hub->http.nc, method, uri);
920  // restore space separator after method and uri
921  *--uri = ' ';
922  *p = ' ';
923  // prepare to complete request
924  end = auth+strlen(auth);
925  }
927  if(req->flags & TCPREQ_KEEPALIVE) {
928  YSTRCPY(end, (int)(req->headerbuf + req->headerbufsize - end), "\r\n");
929  } else {
930  YSTRCPY(end, (int)(req->headerbuf + req->headerbufsize - end), "Connection: close\r\n\r\n");
931  }
932  //write header
933  res = yTcpWrite(req->http.skt, req->headerbuf, (int)strlen(req->headerbuf), errmsg);
934  if(YISERR(res)) {
935  yTcpClose(req->http.skt);
936  req->http.skt = INVALID_SOCKET;
937  return res;
938  }
939  if(req->bodysize > 0){
940  //write body
941  res = yTcpWrite(req->http.skt, req->bodybuf, req->bodysize, errmsg);
942  if(YISERR(res)) {
943  yTcpClose(req->http.skt);
944  req->http.skt = INVALID_SOCKET;
945  TCPLOG("yTcpOpenReqEx write failed for Req %p[%x]\n", req, req->http.skt);
946  return res;
947  }
948  }
949  req->write_tm = yapiGetTickCount();
950 
951  if (req->hub->wuce.listensock != INVALID_SOCKET) {
952  return yDringWakeUpSocket(&req->hub->wuce, 1, errmsg);
953  } else {
954  return YAPI_SUCCESS;
955  }
956 }
957 
958 
959 static void yHTTPCloseReqEx(struct _RequestSt *req, int canReuseSocket)
960 {
961  TCPLOG("yHTTPCloseReqEx %p[%d]\n",req, canReuseSocket);
962 
963  // mutex already taken by caller
964  req->flags &= ~TCPREQ_KEEPALIVE;
965  if (req->callback) {
966  u32 len = req->replysize - req->replypos;
967  u8 *ptr = req->replybuf + req->replypos;
968  if (req->errcode == YAPI_NO_MORE_DATA) {
969  req->callback(req->context, ptr, len, YAPI_SUCCESS, "");
970  } else {
971  req->callback(req->context, ptr, len, req->errcode, req->errmsg);
972  }
973  req->callback = NULL;
974  // ASYNC Request are automaticaly released
975  req->flags &= ~TCPREQ_IN_USE;
976  }
977 
978  if(req->http.skt != INVALID_SOCKET) {
979  if (canReuseSocket) {
980  req->http.reuseskt = req->http.skt;
981  } else {
982  yTcpClose(req->http.skt);
983  }
984  req->http.skt = INVALID_SOCKET;
985  }
986  ySetEvent(&req->finished);
987 }
988 
989 
990 static int yHTTPMultiSelectReq(struct _RequestSt **reqs, int size, u64 ms, WakeUpSocket *wuce, char *errmsg)
991 {
992  fd_set fds;
993  struct timeval timeout;
994  int res,i;
995  YSOCKET sktmax=0;
996 
997  memset(&timeout, 0, sizeof(timeout));
998  timeout.tv_sec = (long)ms/1000;
999  timeout.tv_usec = (int)(ms % 1000) *1000;
1000  /* wait for data */
1001  //dbglog("select %p\n", reqs);
1002 
1003 
1004  FD_ZERO(&fds);
1005  if (wuce) {
1006  //dbglog("listensock %p %d\n", reqs, wuce->listensock);
1007  FD_SET(wuce->listensock, &fds);
1008  sktmax = wuce->listensock;
1009  }
1010  for (i = 0; i < size; i++) {
1011  struct _RequestSt *req;
1012  req = reqs[i];
1013  YASSERT(req->proto == PROTO_AUTO || req->proto == PROTO_HTTP);
1014  if(req->http.skt == INVALID_SOCKET) {
1015  return YERR(YAPI_INVALID_ARGUMENT);
1016  } else {
1017  //dbglog("sock %p %p:%d\n", reqs, req, req->http.skt);
1018  FD_SET(req->http.skt, &fds);
1019  if(req->http.skt > sktmax)
1020  sktmax = req->http.skt;
1021  }
1022  }
1023  if (sktmax == 0) {
1024  return YAPI_SUCCESS;
1025  }
1026  res = select((int)sktmax + 1, &fds, NULL, NULL, &timeout);
1027  if (res < 0) {
1028 #ifndef WINDOWS_API
1029  if(SOCK_ERR == EAGAIN){
1030  return 0;
1031  } else
1032 #endif
1033  {
1034  res = yNetSetErr();
1035  for (i = 0; i < size; i++) {
1036  TCPLOG("yHTTPSelectReq %p[%X] (%s)\n", reqs[i], reqs[i]->http.skt, errmsg);
1037  }
1038  return res;
1039  }
1040  }
1041  if (res != 0) {
1042  if (wuce && FD_ISSET(wuce->listensock,&fds)) {
1043  YPROPERR(yConsumeWakeUpSocket(wuce, errmsg));
1044  }
1045  for (i = 0; i < size; i++) {
1046  struct _RequestSt *req;
1047  req = reqs[i];
1048  if (FD_ISSET(req->http.skt, &fds)) {
1050  if (req->replysize >= req->replybufsize - 256) {
1051  // need to grow receive buffer
1052  int newsize = req->replybufsize << 1;
1053  u8 *newbuf = (u8*) yMalloc(newsize);
1054  memcpy(newbuf, req->replybuf, req->replysize);
1055  yFree(req->replybuf);
1056  req->replybuf = newbuf;
1057  req->replybufsize = newsize;
1058  }
1059  res = yTcpRead(req->http.skt, req->replybuf + req->replysize, req->replybufsize - req->replysize, errmsg);
1060  //dbglog("check %x:%x:%X\n", check, check2, size);
1061 
1062  req->read_tm = yapiGetTickCount();
1063  if (res < 0) {
1064  // any connection closed by peer ends up with YAPI_NO_MORE_DATA
1065  req->replypos = 0;
1066  req->errcode = YERRTO((YRETCODE) res,req->errmsg);
1067  TCPLOG("yHTTPSelectReq %p[%x] connection closed by peer\n",req,req->http.skt);
1068  yHTTPCloseReqEx(req, 0);
1069  } else if (res > 0) {
1070  req->replysize += res;
1071  if(req->replypos < 0) {
1072  // Need to analyze http headers
1073  if(req->replysize == 8 && !memcmp(req->replybuf, "0K\r\n\r\n\r\n", 8)) {
1074  TCPLOG("yHTTPSelectReq %p[%x] untrashort reply\n",req,req->http.skt);
1075  // successful abbreviated reply (keepalive)
1076  req->replypos = 0;
1077  req->replybuf[0] = 'O';
1078  req->errcode = YERRTO(YAPI_NO_MORE_DATA, req->errmsg);
1079  yHTTPCloseReqEx(req, 1);
1080  } else if(req->replysize >= 4 && !memcmp(req->replybuf, "OK\r\n", 4)) {
1081  // successful short reply, let it go through
1082  req->replypos = 0;
1083  } else if(req->replysize >= 12) {
1084  if(memcmp(req->replybuf, "HTTP/1.1 401", 12) != 0) {
1085  // no authentication required, let it go through
1086  req->replypos = 0;
1087  } else {
1088  // authentication required, process authentication headers
1089  char *method = NULL, *realm = NULL, *qop = NULL, *nonce = NULL, *opaque = NULL;
1090 
1091  if(!req->hub->http.s_user || req->retryCount++ > 3) {
1092  // No credential provided, give up immediately
1093  req->replypos = 0;
1094  req->replysize = 0;
1095  req->errcode = YERRTO(YAPI_UNAUTHORIZED, req->errmsg);
1096  yHTTPCloseReqEx(req, 0);
1097  } else if(yParseWWWAuthenticate((char*)req->replybuf, req->replysize, &method, &realm, &qop, &nonce, &opaque) >= 0) {
1098  // Authentication header fully received, we can close the connection
1099  if (!strcmp(method, "Digest") && !strcmp(qop, "auth")) {
1100  // partial close to reopen with authentication settings
1101  yTcpClose(req->http.skt);
1102  req->http.skt = INVALID_SOCKET;
1103  // device requests Digest qop-authentication, good
1105  yDupSet(&req->hub->http.s_realm, realm);
1106  yDupSet(&req->hub->http.s_nonce, nonce);
1107  yDupSet(&req->hub->http.s_opaque, opaque);
1108  if (req->hub->http.s_user && req->hub->http.s_pwd) {
1109  ComputeAuthHA1(req->hub->http.s_ha1, req->hub->http.s_user, req->hub->http.s_pwd, req->hub->http.s_realm);
1110  }
1111  req->hub->http.nc = 0;
1113  // reopen connection with proper auth parameters
1114  // callback and context parameters are preserved
1115  req->errcode = yHTTPOpenReqEx(req, req->timeout_tm, req->errmsg);
1116  if (YISERR(req->errcode)) {
1117  yHTTPCloseReqEx(req, 0);
1118  }
1119  } else {
1120  // unsupported authentication method for devices, give up
1121  req->replypos = 0;
1122  req->errcode = YERRTO(YAPI_UNAUTHORIZED, req->errmsg);
1123  yHTTPCloseReqEx(req, 0);
1124  }
1125  }
1126  }
1127  }
1128  }
1129  if (req->errcode == YAPI_SUCCESS) {
1130  req->errcode = yTcpCheckReqTimeout(req, req->errmsg);
1131  }
1132  }
1134  }
1135  }
1136  }
1137 
1138  return YAPI_SUCCESS;
1139 }
1140 
1141 
1142 /********************************************************************************
1143 * WebSocket implementation for generic requests
1144 *******************************************************************************/
1145 
1146 static int yWSOpenReqEx(struct _RequestSt *req, int tcpchan, u64 mstimeout, char *errmsg)
1147 {
1148  HubSt *hub = req->hub;
1149  RequestSt *r;
1150  int headlen;
1151  u8 *p;
1152  YASSERT(req->proto == PROTO_WEBSOCKET);
1153 
1154 
1155  if (req->hub->ws.base_state != WS_BASE_CONNECTED) {
1156  return YERRMSG(YAPI_IO_ERROR, "Hub is not ready (WebSocket)");
1157  }
1158 
1159  // merge first line and header
1160  headlen = YSTRLEN(req->headerbuf);
1161  req->ws.requestsize = headlen + 4 + req->bodysize;
1162  req->ws.requestbuf = yMalloc(req->ws.requestsize);
1163  p = req->ws.requestbuf;
1164  memcpy(p, req->headerbuf, headlen);
1165  p += headlen;
1166  //todo: create request buffer more efficiently
1167  if (req->bodysize) {
1168  memcpy(p, req->bodybuf, req->bodysize);
1169  } else {
1170  memcpy(p, "\r\n\r\n", 4);
1171  }
1172  if (req->callback) {
1174  req->ws.asyncId = hub->ws.s_next_async_id++;
1175  if (hub->ws.s_next_async_id >= 127) {
1176  hub->ws.s_next_async_id = 48;
1177  }
1179  }
1180  req->ws.channel = tcpchan;
1181  req->timeout_tm = mstimeout;
1182  //WSLOG("req(%s:%p): open req chan=%d timeout=%dms asyncId=%d\n", req->hub->name, req, tcpchan, (int)mstimeout, req->ws.asyncId);
1183  YASSERT(tcpchan < MAX_ASYNC_TCPCHAN);
1184  yEnterCriticalSection(&hub->ws.chan[tcpchan].access);
1185  req->ws.next = NULL; // just in case
1186  if (hub->ws.chan[tcpchan].requests) {
1187  r = hub->ws.chan[tcpchan].requests;
1188  while (r->ws.next) {
1189  r =r->ws.next;
1190  }
1191  r->ws.next = req;
1192  } else {
1193  hub->ws.chan[tcpchan].requests = req;
1194  }
1195  yLeaveCriticalSection(&hub->ws.chan[tcpchan].access);
1196  req->write_tm = yapiGetTickCount();
1197  return yDringWakeUpSocket(&hub->wuce, 1, errmsg);
1198 }
1199 
1200 
1201 
1202 static int yWSSelectReq(struct _RequestSt* req, u64 mstimeout, char* errmsg)
1203 {
1204  int done = yWaitForEvent(&req->finished, (int)mstimeout);
1205 
1206  REQLOG("ws_req:%p: select for %d ms %d\n", req, (int)mstimeout, done);
1207 
1208  if (done) {
1209  req->errcode = YAPI_NO_MORE_DATA;
1210  }
1211  return YAPI_SUCCESS;
1212 }
1213 
1214 
1215 static void yWSCloseReqEx(struct _RequestSt *req, int takeCS)
1216 {
1217  HubSt *hub = req->hub;
1218  RequestSt *r, *p;
1219  int tcpchan;
1220  u32 len;
1221  u8 *ptr;
1222 #ifdef DEBUG_WEBSOCKET
1223  u64 duration;
1224  duration = yapiGetTickCount() - req->open_tm;
1225  WSLOG("req(%s:%p) close req after %"FMTu64"ms (%"FMTu64"ms) with %d bytes errcode = %d\n", req->hub->name, req, duration, (req->write_tm - req->open_tm), req->replysize, req->errcode);
1226 #endif
1227 
1228  YASSERT(req->proto == PROTO_WEBSOCKET);
1229  if (req->callback) {
1230  // async close
1231  len = req->replysize - req->replypos;
1232  ptr = req->replybuf + req->replypos;
1233  if (req->errcode == YAPI_NO_MORE_DATA) {
1234  req->callback(req->context, ptr, len, YAPI_SUCCESS, "");
1235  } else {
1236  req->callback(req->context, ptr, len, req->errcode, req->errmsg);
1237  }
1238  req->callback = NULL;
1239  }
1240 
1241 
1242  tcpchan = req->ws.channel;
1243  YASSERT(tcpchan < MAX_ASYNC_TCPCHAN);
1244  if (takeCS) {
1245  yEnterCriticalSection(&hub->ws.chan[tcpchan].access);
1246  }
1247  r = hub->ws.chan[tcpchan].requests;
1248  p = NULL;
1249  while(r && r !=req) {
1250  p = r;
1251  r = r->ws.next;
1252 
1253  }
1254  YASSERT(r);
1255  if (r) {
1256  if (p == NULL) {
1257  hub->ws.chan[tcpchan].requests = r->ws.next;
1258  } else {
1259  p->ws.next = r->ws.next;
1260  }
1261  }
1262  if (takeCS) {
1263  yLeaveCriticalSection(&hub->ws.chan[tcpchan].access);
1264  }
1265 }
1266 
1267 
1268 
1269 /********************************************************************************
1270 * Generic Request funtions (HTTP or WS)
1271 *******************************************************************************/
1272 
1273 #ifdef TRACE_TCP_REQ
1274 
1275 static void dumpTCPReq(const char *fileid, int lineno, struct _RequestSt *req)
1276 {
1277  int w;
1278  int has_cs =yTryEnterCriticalSection(&req->access);
1279  const char *proto;
1280  const char *state;
1281 
1282  dbglog("dump TCPReq %p from %s:%d\n", req, fileid, lineno);
1283  if (req->hub){
1284  dbglog("Hub: %s\n", req->hub->name);
1285  } else{
1286  dbglog("Hub: null\n");
1287  }
1288 
1289 
1290  switch (req->state) {
1291  case REQ_CLOSED:
1292  state ="state=REQ_CLOSED";
1293  break;
1294  case REQ_OPEN:
1295  state ="state=REQ_OPEN";
1296  break;
1297  case REQ_CLOSED_BY_HUB:
1298  state ="state=REQ_CLOSED_BY_HUB";
1299  break;
1300  case REQ_CLOSED_BY_API:
1301  state ="state=REQ_CLOSED_BY_API";
1302  break;
1303  case REQ_ERROR:
1304  state ="state=REQ_ERROR";
1305  break;
1306  default:
1307  state ="state=??";
1308  break;
1309  }
1310 
1311  dbglog("%s retcode=%d (retrycount=%d) errmsg=%s\n", state, req->errcode, req->retryCount, req->errmsg);
1312  switch(req->proto){
1313  case PROTO_AUTO: proto ="PROTO_AUTO"; break;
1314  case PROTO_HTTP: proto ="PROTO_HTTP"; break;
1315  case PROTO_WEBSOCKET: proto ="PROTO_WEBSOCKET"; break;
1316  default: proto ="unk"; break;
1317  }
1318  dbglog("proto=%s socket=%x (reuse=%x) flags=%x\n", proto, req->http.skt, req->http.reuseskt, req->flags);
1319  dbglog("time open=%"FMTx64" last read=%"FMTx64" last write=%"FMTx64" timeout=%"FMTx64"\n", req->open_tm, req->read_tm, req->write_tm, req->timeout_tm);
1320  dbglog("readed=%d (readpos=%d)\n", req->replysize, req->replysize);
1321  dbglog("callback=%p context=%p\n", req->callback, req->context);
1322  if (req->headerbuf){
1323  dbglog("req[%s]\n", req->headerbuf);
1324  } else {
1325  dbglog("null\n");
1326  }
1327  w = yWaitForEvent(&req->finished, 0);
1328  dbglog("finished=%d\n", w);
1329  if (has_cs) {
1331  }
1332 
1333 }
1334 #endif
1335 
1336 
1337 struct _RequestSt* yReqAlloc(struct _HubSt *hub)
1338 {
1339  struct _RequestSt* req = yMalloc(sizeof(struct _RequestSt));
1340  memset(req, 0, sizeof(struct _RequestSt));
1341  yHashGetUrlPort(hub->url, NULL, NULL, &req->proto, NULL, NULL);
1342  TCPLOG("yTcpInitReq %p[%x:%x]\n", req, hub->url, req->proto);
1343  req->replybufsize = 1500;
1344  req->replybuf = (u8*)yMalloc(req->replybufsize);
1346  yCreateManualEvent(&req->finished, 1);
1347  req->hub = hub;
1348  switch (req->proto) {
1349  case PROTO_AUTO:
1350  case PROTO_HTTP:
1351  req->http.reuseskt = INVALID_SOCKET;
1352  req->http.skt = INVALID_SOCKET;
1353  break;
1354  case PROTO_WEBSOCKET:
1355  break;
1356  }
1357  return req;
1358 }
1359 
1360 
1361 
1362 int yReqOpen(struct _RequestSt *req, int wait_for_start, int tcpchan, const char *request, int reqlen, u64 mstimeout,yapiRequestAsyncCallback callback, void *context, RequestProgress progress_cb, void *progress_ctx, char *errmsg)
1363 {
1364  int minlen, i, res;
1365  u64 startwait;
1366 
1367  YPERF_TCP_ENTER(TCPOpenReq);
1368  if (wait_for_start <= 0) {
1370  if (req->flags & TCPREQ_IN_USE) {
1372  return YERR(YAPI_DEVICE_BUSY);
1373  }
1374  } else {
1375  YPERF_TCP_ENTER(TCPOpenReq_wait);
1377  startwait = yapiGetTickCount();
1378  while (req->flags & TCPREQ_IN_USE) {
1379  u64 duration;
1380  // There is an ongoing request to be finished
1382  duration = yapiGetTickCount() - startwait;
1383  if (duration > wait_for_start) {
1384  dbglog("Last request in not finished after %"FMTu64" ms\n", duration);
1385 #ifdef TRACE_TCP_REQ
1386  dumpTCPReq(__FILE_ID__, __LINE__, req);
1387 #endif
1388  return YERRMSG(YAPI_TIMEOUT, "last TCP request is not finished");
1389  }
1390  yWaitForEvent(&req->finished, 100);
1392  }
1393  YPERF_TCP_LEAVE(TCPOpenReq_wait);
1394  }
1395 
1396 
1397  req->flags = 0;
1398  if (request[0] == 'G' && request[1] == 'E' && request[2] == 'T') {
1399  //for GET request discard all exept the first line
1400  for (i = 0; i < reqlen; i++) {
1401  if (request[i] == '\r') {
1402  reqlen = i;
1403  break;
1404  }
1405  }
1406  if (i > 3) {
1407  if (request[i - 3] == '&' && request[i - 2] == '.' && request[i - 1] == ' ') {
1408  req->flags |= TCPREQ_KEEPALIVE;
1409  }
1410  }
1411  req->bodysize = 0;
1412  } else {
1413  const char *p = request;
1414  int bodylen = reqlen - 4;
1415 
1416  while (bodylen > 0 && (p[0] != '\r' || p[1] != '\n' ||
1417  p[2] != '\r' || p[3] != '\n')) {
1418  p++, bodylen--;
1419  }
1420  p += 4;
1421  reqlen = (int)(p - request);
1422  // Build a request body buffer
1423  if (req->bodybufsize < bodylen) {
1424  if (req->bodybuf) yFree(req->bodybuf);
1425  req->bodybufsize = bodylen + (bodylen >> 1);
1426  req->bodybuf = (char*)yMalloc(req->bodybufsize);
1427  }
1428  memcpy(req->bodybuf, p, bodylen);
1429  req->bodysize = bodylen;
1430  }
1431  // Build a request buffer with at least a terminal NUL but
1432  // include space for Connection: close and Authorization: headers
1433  minlen = reqlen + 400;
1434  if (req->headerbufsize < minlen) {
1435  if (req->headerbuf) yFree(req->headerbuf);
1436  req->headerbufsize = minlen + (reqlen >> 1);
1437  req->headerbuf = (char*)yMalloc(req->headerbufsize);
1438  }
1439  memcpy(req->headerbuf, request, reqlen);
1440  req->headerbuf[reqlen] = 0;
1441  req->retryCount = 0;
1442  req->callback = callback;
1443  req->context = context;
1444  req->progressCb = progress_cb;
1445  req->progressCtx = progress_ctx;
1446  req->read_tm = req->write_tm = req->open_tm = yapiGetTickCount();
1447  req->timeout_tm = mstimeout;
1448 
1449 
1450 
1451  // Really build and send the request
1452  if (req->proto == PROTO_AUTO || req->proto == PROTO_HTTP) {
1453  res = yHTTPOpenReqEx(req, mstimeout, errmsg);
1454  } else {
1455  res = yWSOpenReqEx(req, tcpchan, mstimeout, errmsg);
1456  }
1457  if(res == YAPI_SUCCESS) {
1458  req->errmsg[0] = '\0';
1459  req->flags |= TCPREQ_IN_USE;
1460  yResetEvent(&req->finished);
1461  req->state = REQ_OPEN;
1462  }
1463 
1465 
1466  YPERF_TCP_LEAVE(TCPOpenReq);
1467  return res;
1468 }
1469 
1470 int yReqSelect(struct _RequestSt *tcpreq, u64 ms, char *errmsg)
1471 {
1472  if (tcpreq->proto == PROTO_AUTO || tcpreq->proto == PROTO_HTTP) {
1473  return yHTTPMultiSelectReq(&tcpreq, 1, ms, NULL, errmsg);
1474  } else {
1475  return yWSSelectReq(tcpreq, ms, errmsg);
1476  }
1477 }
1478 
1479 int yReqMultiSelect(struct _RequestSt **tcpreq, int size, u64 ms, WakeUpSocket *wuce, char *errmsg)
1480 {
1481  // multi select make no sense in Websocket since all data comme from the same socket
1482  return yHTTPMultiSelectReq(tcpreq, size, ms, wuce, errmsg);
1483 }
1484 
1485 
1486 int yReqIsEof(struct _RequestSt *req, char *errmsg)
1487 {
1488  int res;
1490  if(req->errcode == YAPI_NO_MORE_DATA) {
1491  res = 1;
1492  } else if(req->errcode == 0) {
1493  res = req->errcode = yTcpCheckReqTimeout(req, errmsg);
1494  } else if(req->errcode == YAPI_UNAUTHORIZED) {
1495  res = YERRMSG((YRETCODE) req->errcode, "Access denied, authorization required");
1496  } else {
1497  res = YERRMSG((YRETCODE) req->errcode, req->errmsg);
1498  }
1500  return res;
1501 }
1502 
1503 
1504 int yReqGet(struct _RequestSt *req, u8 **buffer)
1505 {
1506  int avail;
1507 
1509  yTcpCheckReqTimeout(req, req->errmsg);
1510  if(req->replypos < 0) {
1511  // data is not yet ready to consume (still processing header)
1512  avail = 0;
1513  } else {
1514  avail = req->replysize - req->replypos;
1515  if(buffer) {
1516  *buffer = req->replybuf + req->replypos;
1517  }
1518  }
1520 
1521  return avail;
1522 }
1523 
1524 
1525 int yReqRead(struct _RequestSt *req, u8 *buffer, int len)
1526 {
1527  int avail;
1528 
1530  yTcpCheckReqTimeout(req, req->errmsg);
1531  if(req->replypos < 0) {
1532  // data is not yet ready to consume (still processing header)
1533  len = 0;
1534  } else {
1535  avail = req->replysize - req->replypos;
1536  if(len > avail) {
1537  len = avail;
1538  }
1539  if(len && buffer) {
1540  memcpy(buffer, req->replybuf+req->replypos, len);
1541  }
1542  if(req->replypos + len == req->replysize) {
1543  req->replypos = 0;
1544  req->replysize = 0;
1545  if (req->proto == PROTO_WEBSOCKET) {
1546  if (req->state == REQ_CLOSED || req->state == REQ_CLOSED_BY_HUB) {
1547  req->errcode = YAPI_NO_MORE_DATA;
1548  }
1549  }
1550 
1551  } else {
1552  req->replypos += len;
1553  }
1554  }
1556 
1557  return len;
1558 }
1559 
1560 
1561 void yReqClose(struct _RequestSt *req)
1562 {
1563  TCPLOG("yTcpCloseReq %p\n", req);
1564 #if 0
1565  {
1566  u64 now = yapiGetTickCount();
1567  u64 duration = now - req->open_tm;
1568  u64 last_wr = req->write_tm - req->open_tm;
1569  u64 last_rd = req->read_tm - req->open_tm;
1570 
1571  dbglog("request %p total=%"FMTu64"ms (read=%"FMTu64"ms write=%"FMTu64")\n",
1572  req, duration, last_rd, last_wr);
1573  }
1574 #endif
1576  if (req->flags &TCPREQ_IN_USE) {
1577 
1578  if (req->proto == PROTO_AUTO || req->proto == PROTO_HTTP) {
1579  yHTTPCloseReqEx(req, 0);
1580  } else {
1581 #if 0
1582  u64 last = req->ws.last_write_tm - req->open_tm;
1583  u64 first = req->ws.first_write_tm - req->open_tm;
1584 
1585  dbglog("request.ws %p first_write=%"FMTu64"ms last_write=%"FMTu64")\n",
1586  req, first, last);
1587 #endif
1588  yWSCloseReqEx(req, 1);
1589  }
1590  req->flags &= ~TCPREQ_IN_USE;
1591  }
1593 }
1594 
1595 
1596 int yReqIsAsync(struct _RequestSt *req)
1597 {
1598  int res;
1600  res = (req->flags & TCPREQ_IN_USE) && (req->callback != NULL);
1602  return res;
1603 }
1604 
1605 
1606 void yReqFree(struct _RequestSt *req)
1607 {
1608  TCPLOG("yTcpFreeReq %p\n",req);
1609  if (req->proto == PROTO_AUTO || req->proto == PROTO_HTTP) {
1610  if (req->http.skt != INVALID_SOCKET) {
1611  yTcpClose(req->http.skt);
1612  }
1613  if (req->http.reuseskt != INVALID_SOCKET) {
1614  yTcpClose(req->http.reuseskt);
1615  }
1616  } else {
1617  if (req->ws.requestbuf) yFree(req->ws.requestbuf);
1618  }
1619  if(req->headerbuf) yFree(req->headerbuf);
1620  if(req->bodybuf) yFree(req->bodybuf);
1621  if(req->replybuf) yFree(req->replybuf);
1622  yCloseEvent(&req->finished);
1624  yFree(req);
1625  //memset(req, 0, sizeof(struct _RequestSt));
1626 }
1627 
1628 
1630 {
1631  int i;
1632  RequestSt *req = NULL;
1633 
1634  if (hub->proto == PROTO_AUTO || hub->proto == PROTO_HTTP) {
1635  for (i = 0; i < ALLOC_YDX_PER_HUB; i++) {
1636  req = yContext->tcpreq[i];
1637  if (req && yReqIsAsync(req)) {
1638  return 1;
1639  }
1640  }
1641  } else {
1642  int tcpchan;
1643  for (tcpchan = 0; tcpchan < MAX_ASYNC_TCPCHAN; tcpchan++) {
1644  yEnterCriticalSection(&hub->ws.chan[tcpchan].access);
1645  if (hub->ws.chan[tcpchan].requests) {
1646  req = hub->ws.chan[tcpchan].requests;
1647  while (req && req->ws.requestsize == req->ws.requestpos && req->state == REQ_CLOSED) {
1648  req = req->ws.next;
1649  }
1650  if (req != NULL) {
1651  //dbglog("stil request pending on hub %s (%p)\n", hub->name, req);
1652  yLeaveCriticalSection(&hub->ws.chan[tcpchan].access);
1653  return 1;
1654  }
1655  }
1656  yLeaveCriticalSection(&hub->ws.chan[tcpchan].access);
1657  }
1658  }
1659  return 0;
1660 }
1661 
1662 
1663 
1664 
1665 /********************************************************************************
1666 * Websocket funtions
1667 *******************************************************************************/
1668 
1669 static const char* ws_header_start = " HTTP/1.1\r\nSec-WebSocket-Version: 13\r\nUser-Agent: Yoctopuce\r\nSec-WebSocket-Key: ";
1670 static const char* ws_header_end = "\r\nConnection: keep-alive, Upgrade\r\nUpgrade: websocket\r\n\r\n";
1671 
1672 #define YRand32() rand()
1673 
1674 /*****************************************************************************
1675 Function:
1676 WORD Base64Encode(BYTE* cSourceData, WORD wSourceLen,
1677 BYTE* cDestData, WORD wDestLen)
1678 
1679 Description:
1680 Encodes a binary array to Base-64.
1681 
1682 Precondition:
1683 None
1684 
1685 Parameters:
1686 cSourceData - Pointer to a string of binary data
1687 wSourceLen - Length of the binary source data
1688 cDestData - Pointer to write the Base-64 encoded data
1689 wDestLen - Maximum length that can be written to cDestData
1690 
1691 Returns:
1692 Number of encoded bytes written to cDestData. This will always be
1693 a multiple of 4.
1694 
1695 Remarks:
1696 Encoding cannot be performed in-place. If cSourceData overlaps with
1697 cDestData, the behavior is undefined.
1698 
1699 Encoded data is always at least 1/3 larger than the source data. It may
1700 be 1 or 2 bytes larger than that.
1701 ***************************************************************************/
1702 static u16 Base64Encode(const u8* cSourceData, u16 wSourceLen, u8* cDestData, u16 wDestLen)
1703 {
1704  u8 i, j;
1705  u8 vOutput[4];
1706  u16 wOutputLen;
1707 
1708  wOutputLen = 0;
1709  while (wDestLen >= 4u)
1710  {
1711  // Start out treating the output as all padding
1712  vOutput[0] = 0xFF;
1713  vOutput[1] = 0xFF;
1714  vOutput[2] = 0xFF;
1715  vOutput[3] = 0xFF;
1716 
1717  // Get 3 input octets and split them into 4 output hextets (6-bits each)
1718  if (wSourceLen == 0u)
1719  break;
1720  i = *cSourceData++;
1721  wSourceLen--;
1722  vOutput[0] = (i & 0xFC) >> 2;
1723  vOutput[1] = (i & 0x03) << 4;
1724  if (wSourceLen)
1725  {
1726  i = *cSourceData++;
1727  wSourceLen--;
1728  vOutput[1] |= (i & 0xF0) >> 4;
1729  vOutput[2] = (i & 0x0F) << 2;
1730  if (wSourceLen)
1731  {
1732  i = *cSourceData++;
1733  wSourceLen--;
1734  vOutput[2] |= (i & 0xC0) >> 6;
1735  vOutput[3] = i & 0x3F;
1736  }
1737  }
1738 
1739  // Convert hextets into Base 64 alphabet and store result
1740  for (i = 0; i < 4u; i++)
1741  {
1742  j = vOutput[i];
1743 
1744  if (j <= 25u)
1745  j += 'A' - 0;
1746  else if (j <= 51u)
1747  j += 'a' - 26;
1748  else if (j <= 61u)
1749  j += '0' - 52;
1750  else if (j == 62u)
1751  j = '+';
1752  else if (j == 63u)
1753  j = '/';
1754  else // Padding
1755  j = '=';
1756 
1757  *cDestData++ = j;
1758  }
1759 
1760  // Update counters
1761  wDestLen -= 4;
1762  wOutputLen += 4;
1763  }
1764 
1765  return wOutputLen;
1766 }
1767 
1768 
1769 
1770 
1771 
1772 
1773 
1774 /********************************************************************************
1775 * WebSocket internal function
1776 *******************************************************************************/
1777 
1778 
1779 //todo : factorise GenereateWebSockeyKey + VerifyWebsocketKey
1780 
1781 // compute a new nonce for http request
1782 // the buffer passed as argument must be at least 28 bytes long
1783 static int GenereateWebSockeyKey(const u8 *url, u32 urllen, char *buffer)
1784 {
1785  u32 salt[2];
1786  HASH_SUM ctx;
1787  u8 rawbuff[16];
1788 
1789  // Our nonce is base64_encoded [ MD5( Rand32,(ytime^Rand), ) ]
1790  salt[0] = YRand32();
1791  salt[1] = yapiGetTickCount() & 0xff;
1792  MD5Initialize(&ctx);
1793  MD5AddData(&ctx, (u8*)salt, 2);
1794  MD5AddData(&ctx,url, urllen);
1795  MD5Calculate(&ctx, rawbuff);
1796  return Base64Encode(rawbuff, 16, (u8*)buffer, 28);
1797 }
1798 
1799 
1800 static int VerifyWebsocketKey(const char *data, u16 hdrlen, const char *reference, u16 reference_len)
1801 {
1802  u8 buf[80];
1803  const char *magic = YOCTO_WEBSOCKET_MAGIC;
1804  u8 *sha1;
1805 
1806  // compute correct key
1807  if (hdrlen >= sizeof(buf)) {
1808 #ifndef MICROCHIP_API
1809  dbglog("Bad WebSocket header (%d)\n", hdrlen);
1810 #else
1811  ylog("WS!");
1812 #endif
1813  return 0;
1814  }
1815  memcpy(buf, reference, reference_len);
1816 #ifdef USE_FAR_YFSTR
1817  apiGetStr(magic.hofs, (char*)buf + CbCtx.websocketkey.len);
1818 #else
1819  memcpy(buf + reference_len, magic, YOCTO_WEBSOCKET_MAGIC_LEN + 1);
1820 #endif
1821  sha1 = ySHA1((char *)buf);
1822  Base64Encode(sha1, 20, buf, 80);
1823  if (memcmp(buf, data, hdrlen) == 0) {
1824  return 1;
1825  }
1826  return 0;
1827 }
1828 
1829 
1830 
1831 
1832 
1833 
1834 
1835 
1836 #define WS_CONNEXION_TIMEOUT 10000
1837 #define WS_MAX_DATA_LEN 124
1838 
1839 
1840 /*
1841 * send Websocket frame for a hub
1842 */
1843 static int ws_sendFrame(HubSt *hub, int stream, int tcpchan, const u8 *data, int datalen, char *errmsg)
1844 {
1845  u32 buffer_32[33];
1846  u32 mask;
1847  int i;
1848  WSStreamHead strym;
1849  u8 *p = (u8*)buffer_32;
1850 #ifdef DEBUG_SLOW_TCP
1851  u64 start = yapiGetTickCount();
1852 #endif
1853  int tcp_write_res;
1854 
1855  YASSERT(datalen <= WS_MAX_DATA_LEN);
1856 #ifdef DEBUG_WEBSOCKET
1857  // disable masking for debugging
1858  mask = 0;
1859 #else
1860  mask = YRand32();
1861 #endif
1862  // do not start at offset zero on purpose
1863  // we want the buffer to be aligned on u32
1864  p[0] = 0x82;
1865  p[1] = (u8)(datalen + 1) | 0x80;;
1866  p[2] = ((u8*)&mask)[2];
1867  p[3] = ((u8*)&mask)[3];
1868 
1869  p[4] = ((u8*)&mask)[0];
1870  p[5] = ((u8*)&mask)[1];
1871  strym.tcpchan = tcpchan;
1872  strym.stream = stream;
1873  p[6] = strym.encaps ^ p[2];
1874  if (datalen) {
1875  p[7] = *data ^ p[3];
1876  }
1877  if (datalen > 1) {
1878  memcpy(buffer_32 + 2, data + 1, datalen - 1);
1879  for (i = 0; i < (datalen - 1 + 3) >> 2; i++) {
1880  buffer_32[i + 2] ^= mask;
1881  }
1882  }
1883  tcp_write_res = yTcpWrite(hub->ws.skt, (char*)p, datalen + 7, errmsg);
1884 #ifdef DEBUG_SLOW_TCP
1885  u64 delta = yapiGetTickCount() - start;
1886  if (delta > 10) {
1887  dbglog("WS: yTcpWrite took %"FMTu64"ms (stream=%d chan=%d res=%d)\n", delta, strym.stream, strym.tcpchan, tcp_write_res);
1888  }
1889 #endif
1890  return tcp_write_res;
1891 }
1892 
1893 /*
1894 * send authentication meta
1895 */
1897 {
1898  USB_Meta_Pkt meta_out;
1899  memset(&meta_out, 0, sizeof(USB_Meta_Pkt));
1901 
1902 #if 1
1904  meta_out.auth.version = USB_META_WS_PROTO_V1;
1905  } else {
1906  meta_out.auth.version = USB_META_WS_PROTO_V2;
1907  }
1908 #else
1909  meta_out.auth.version = USB_META_WS_PROTO_V1;
1910 #endif
1911  if (hub->ws.user != INVALID_HASH_IDX && hub->ws.pass != INVALID_HASH_IDX) {
1912  u8 ha1[16];
1913  const char * user = yHashGetStrPtr(hub->ws.user);
1914  const char * pass = yHashGetStrPtr(hub->ws.pass);
1916  meta_out.auth.nonce = INTEL_U32(hub->ws.nounce);
1917  ComputeAuthHA1(ha1, user, pass, hub->ws.serial);
1918  CheckWSAuth(hub->ws.remoteNounce, ha1, NULL, meta_out.auth.sha1);
1919  }
1920  return ws_sendFrame(hub,YSTREAM_META ,0, (const u8*) &meta_out, USB_META_WS_AUTHENTICATION_SIZE, errmsg);
1921 }
1922 
1923 static void ws_appendTCPData(RequestSt* req, u8* buffer, int pktlen, int isClose)
1924 {
1925  if (pktlen) {
1926  if (req->replybufsize < req->replysize + pktlen) {
1927  u8 *newbuff;
1928  req->replybufsize <<= 1;
1929  newbuff = yMalloc(req->replybufsize);
1930  memcpy(newbuff, req->replybuf, req->replysize);
1931  yFree(req->replybuf);
1932  req->replybuf = newbuff;
1933  }
1934 
1935  memcpy(req->replybuf + req->replysize, buffer, pktlen);
1936  req->replysize += pktlen;
1937  }
1938  req->read_tm = yapiGetTickCount();
1939  if (isClose) {
1940  req->state = REQ_CLOSED;
1941  ySetEvent(&req->finished);
1942  if (req->callback != NULL) {
1943  // async request are automaticaly closed
1944  yWSCloseReqEx(req, 0);
1945  yReqFree(req);
1946  }
1947  }
1948 
1949 }
1950 
1951 /*
1952 * ws_parseIncommingFrame parse incomming Websocket frame
1953 */
1954 static int ws_parseIncommingFrame(HubSt *hub, u8 *buffer, int pktlen, char *errmsg)
1955 {
1956  WSStreamHead strym;
1957  RequestSt *req;
1958  int flags;
1959  const char * user;
1960  const char * pass;
1961  int maxtcpws;
1962 #ifdef DEBUG_WEBSOCKET
1963  u64 reltime = yapiGetTickCount() - hub->ws.connectionTime;
1964 #endif
1965 
1966  YASSERT(pktlen > 0);
1967  strym.encaps = buffer[0];
1968  buffer++;
1969  pktlen--;
1970  switch (strym.stream) {
1971  case YSTREAM_TCP_NOTIF:
1972  if (pktlen > 0) {
1973 #if 0
1974  {
1975  FILE *f;
1976  //printf("%s", buffer);
1977  YASSERT(YFOPEN(&f, "req_trace\\api_not.txt", "ab") == 0);
1978  fwrite(buffer, 1, pktlen, f);
1979  fclose(f);
1980  }
1981 #endif
1982  yPushFifo(&hub->not_fifo, buffer, pktlen);
1983  while (handleNetNotification(hub));
1984  }
1985  break;
1986  case YSTREAM_EMPTY:
1987  return YAPI_SUCCESS;
1989  yEnterCriticalSection(&hub->ws.chan[strym.tcpchan].access);
1990  req = hub->ws.chan[strym.tcpchan].requests;
1991  while (req != NULL && req->state != REQ_OPEN && req->state != REQ_CLOSED_BY_HUB) {
1992  req = req->ws.next;
1993  }
1994  if (req == NULL) {
1995  dbglog("Drop incomming TCP trafic on channel (%d/%d)\n", strym.stream, strym.tcpchan);
1996  } else {
1997  int asyncid = buffer[pktlen - 1];
1998  pktlen--;
1999  if (req->ws.asyncId != asyncid) {
2000  dbglog("WS: Incorrect async-close signature on tcpChan %d (%d)\n", strym.tcpchan, asyncid);
2001  break;
2002  }
2003  WSLOG("req(%s:%p) close async %d\n", req->hub->name, req, req->ws.asyncId);
2004  ws_appendTCPData(req, buffer, pktlen, 1);
2005  }
2006  yLeaveCriticalSection(&hub->ws.chan[strym.tcpchan].access);
2007  break;
2008  case YSTREAM_TCP:
2009  case YSTREAM_TCP_CLOSE:
2010  yEnterCriticalSection(&hub->ws.chan[strym.tcpchan].access);
2011  req = hub->ws.chan[strym.tcpchan].requests;
2012  while (req != NULL && req->state!= REQ_OPEN && req->state != REQ_CLOSED_BY_HUB) {
2013  req = req->ws.next;
2014  }
2015  if (req == NULL) {
2016  dbglog("Drop incomming TCP trafic on channel (%d/%d)\n", strym.stream, strym.tcpchan);
2017  } else {
2018  if (strym.stream == YSTREAM_TCP_CLOSE) {
2019  int res = ws_sendFrame(hub, YSTREAM_TCP_CLOSE, strym.tcpchan, NULL, 0, errmsg);
2020  if (res < 0) {
2021  WSLOG("req(%s:%p) unable to ack remote close (%d/%s)\n", req->hub->name, req, res, errmsg);
2022  }
2023  //WSLOG("req(%s:%p) close\n", req->hub->name, req);
2024  }
2025  ws_appendTCPData(req, buffer, pktlen, strym.stream == YSTREAM_TCP_CLOSE);
2026  }
2027  yLeaveCriticalSection(&hub->ws.chan[strym.tcpchan].access);
2028  break;
2029  case YSTREAM_META: {
2030  USB_Meta_Pkt *meta = (USB_Meta_Pkt*)(buffer);
2031  WSLOG("%"FMTu64": META type=%d len=%d\n",reltime, meta->announce.metaType, pktlen);
2032  switch (meta->announce.metaType) {
2033  case USB_META_WS_ANNOUNCE:
2035  return YAPI_SUCCESS;
2036  }
2037  hub->ws.remoteVersion = meta->announce.version;
2038  hub->ws.remoteNounce = INTEL_U32(meta->announce.nonce);
2039  maxtcpws = INTEL_U16(meta->announce.maxtcpws);
2040  if (maxtcpws > 0) {
2041  hub->ws.tcpMaxWindowSize = maxtcpws;
2042  }
2044  WSLOG("hub(%s) Announce: %s (v%d / %x)\n", hub->name, meta->announce.serial, meta->announce.version, hub->ws.remoteNounce);
2045  hub->ws.nounce = YRand32();
2048  return ws_sendAuthenticationMeta(hub, errmsg);
2050  if (hub->ws.base_state != WS_BASE_AUTHENTICATING)
2051  return YAPI_SUCCESS;
2052  if (meta->auth.version < USB_META_WS_PROTO_V1 || (u32)pktlen < USB_META_WS_AUTHENTICATION_SIZE) {
2053  return YAPI_SUCCESS;
2054  }
2055  hub->ws.tcpRoundTripTime = (u32)(yapiGetTickCount() - hub->ws.connectionTime + 1);
2056  if(hub->ws.tcpMaxWindowSize < 2048 && hub->ws.tcpRoundTripTime < 7) {
2057  // Fix overly optimistic round-trip on YoctoHubs
2058  hub->ws.tcpRoundTripTime = 7;
2059  }
2060 #ifdef DEBUG_WEBSOCKET
2061  {
2062  int uploadRate = hub->ws.tcpMaxWindowSize * 1000 / hub->ws.tcpRoundTripTime;
2063  dbglog("RTT=%dms, WS=%d, uploadRate=%f KB/s\n", hub->ws.tcpRoundTripTime, hub->ws.tcpMaxWindowSize, uploadRate/1000.0);
2064  }
2065 #endif
2066 
2067  flags = meta->auth.flags;
2068  if ((flags & USB_META_WS_AUTH_FLAGS_RW) != 0) {
2069  hub->rw_access = 1;
2070  }
2071  if (hub->ws.user != INVALID_HASH_IDX) {
2072  user = yHashGetStrPtr(hub->ws.user);
2073  }else {
2074  user = "";
2075  }
2076 
2077  if (hub->ws.pass != INVALID_HASH_IDX) {
2078  pass = yHashGetStrPtr(hub->ws.pass);
2079  } else {
2080  pass = "";
2081  }
2082  if ((flags & USB_META_WS_AUTH_FLAGS_VALID) != 0) {
2083  u8 ha1[16];
2084  ComputeAuthHA1(ha1, user, pass, hub->ws.serial);
2085  if (CheckWSAuth(hub->ws.nounce, ha1, meta->auth.sha1, NULL)) {
2087  hub->state = NET_HUB_ESTABLISHED;
2088  hub->retryCount = 0;
2089  hub->attemptDelay = 500;
2090  WSLOG("hub(%s): connected as %s\n", hub->name, user);
2091  } else {
2092  YSPRINTF(errmsg, YOCTO_ERRMSG_LEN, "Authentication as %s failed (%s:%d)", user, __FILE_ID__, __LINE__);
2093  return YAPI_UNAUTHORIZED;
2094  }
2095  } else {
2096  if (hub->ws.user == INVALID_HASH_IDX) {
2098  hub->state = NET_HUB_ESTABLISHED;
2099  hub->retryCount = 0;
2100  hub->attemptDelay = 500;
2101  WSLOG("hub(%s): connected\n",hub->name);
2102  } else {
2103  if (YSTRCMP(user,"admin")==0 && !hub->rw_access) {
2104  YSPRINTF(errmsg, YOCTO_ERRMSG_LEN, "Authentication as %s failed", user);
2105  } else {
2106  YSPRINTF(errmsg, YOCTO_ERRMSG_LEN, "Authentication error : hub has no password for %s", user);
2107  }
2108  return YAPI_UNAUTHORIZED;
2109  }
2110  }
2111  break;
2112  case USB_META_WS_ERROR:
2113  if (INTEL_U16(meta->error.htmlcode) == 401) {
2114  return YERR(YAPI_UNAUTHORIZED);
2115  } else {
2116  YSPRINTF(errmsg, YOCTO_ERRMSG_LEN, "Remote hub closed connection with error %d", INTEL_U16(meta->error.htmlcode));
2117  return YAPI_IO_ERROR;
2118  }
2119  case USB_META_ACK_UPLOAD:
2120  {
2121  int tcpchan = meta->uploadAck.tcpchan;
2122  yEnterCriticalSection(&hub->ws.chan[tcpchan].access);
2123  req = hub->ws.chan[tcpchan].requests;
2124  while (req != NULL && req->state != REQ_OPEN && req->state != REQ_CLOSED_BY_HUB) {
2125  req = req->ws.next;
2126  }
2127  if (req) {
2128  u32 ackBytes = meta->uploadAck.totalBytes[0] + (meta->uploadAck.totalBytes[1] << 8) + (meta->uploadAck.totalBytes[2] << 16) + (meta->uploadAck.totalBytes[3] << 24);
2129  u64 ackTime = yapiGetTickCount();
2130  if (hub->ws.chan[tcpchan].lastUploadAckTime && ackBytes > hub->ws.chan[tcpchan].lastUploadAckBytes) {
2131  int deltaBytes;
2132  u64 deltaTime;
2133  u32 newRate;
2134  hub->ws.chan[tcpchan].lastUploadAckBytes = ackBytes;
2135  hub->ws.chan[tcpchan].lastUploadAckTime = ackTime;
2136 
2137  deltaBytes = ackBytes - hub->ws.chan[tcpchan].lastUploadRateBytes;
2138  deltaTime = ackTime - hub->ws.chan[tcpchan].lastUploadRateTime;
2139  WSLOG("delta bytes=%d time=%"FMTu64"ms\n",deltaBytes, deltaTime);
2140 
2141 
2142  if (deltaTime < 500) {
2143  yLeaveCriticalSection(&hub->ws.chan[tcpchan].access);
2144  break; // wait more
2145  }
2146  if (deltaTime < 1000 && deltaBytes < 65536) {
2147  yLeaveCriticalSection(&hub->ws.chan[tcpchan].access);
2148  break; // wait more
2149  }
2150  hub->ws.chan[tcpchan].lastUploadRateBytes = ackBytes;
2151  hub->ws.chan[tcpchan].lastUploadRateTime = ackTime;
2152  if (req->progressCb && req->ws.requestsize) {
2153  req->progressCb(req->progressCtx, ackBytes, req->ws.requestsize);
2154  }
2155  newRate = (u32)(deltaBytes * 1000 / deltaTime);
2156  hub->ws.uploadRate = (u32)(0.8 * hub->ws.uploadRate + 0.3 * newRate);
2157  WSLOG("New rate: %.2f KB/s (based on %.2f KB in %.2fs)\n", hub->ws.uploadRate / 1000.0, deltaBytes / 1000.0, deltaTime / 1000.0);
2158  } else {
2159  WSLOG("First Ack received (rate=%d)\n", hub->ws.uploadRate);
2160  hub->ws.chan[tcpchan].lastUploadAckBytes = ackBytes;
2161  hub->ws.chan[tcpchan].lastUploadAckTime = ackTime;
2162  hub->ws.chan[tcpchan].lastUploadRateBytes = ackBytes;
2163  hub->ws.chan[tcpchan].lastUploadRateTime = ackTime;
2164  if (req->progressCb && req->ws.requestsize) {
2165  req->progressCb(req->progressCtx, ackBytes, req->ws.requestsize);
2166  }
2167  }
2168  }
2169  yLeaveCriticalSection(&hub->ws.chan[tcpchan].access);
2170  }
2171  break;
2172  default:
2173  WSLOG("unhandled Meta pkt %d\n", meta->announce.metaType);
2174  break;
2175  }
2176  }
2177  break;
2178  case YSTREAM_NOTICE:
2179  case YSTREAM_REPORT:
2180  case YSTREAM_REPORT_V2:
2181  case YSTREAM_NOTICE_V2:
2182  default:
2183  dbglog("Invalid WS stream type (%d)\n", strym.stream);
2184  }
2185  return YAPI_SUCCESS;
2186 }
2187 
2188 // return 1 if there is still a request pending, 0 if all is done, -1 on error
2190 {
2191  int tcpchan;
2192  for (tcpchan = 0; tcpchan < MAX_ASYNC_TCPCHAN; tcpchan++) {
2193  RequestSt *req = NULL;
2194  yEnterCriticalSection(&hub->ws.chan[tcpchan].access);
2195  req = hub->ws.chan[tcpchan].requests;
2196  while (req && req->state == REQ_CLOSED) {
2197  req = req->ws.next;
2198  }
2199  yLeaveCriticalSection(&hub->ws.chan[tcpchan].access);
2200  if (req) {
2201  return 1;
2202  }
2203  }
2204  return 0;
2205 }
2206 
2207 
2208 
2209 /*
2210 * look through all pending request if there is some data that we can send
2211 *
2212 */
2213 static int ws_processRequests(HubSt* hub, char *errmsg)
2214 {
2215  int tcpchan;
2216  int res;
2217 
2218  if (hub->ws.next_transmit_tm && hub->ws.next_transmit_tm > yapiGetTickCount()) {
2219  return YAPI_SUCCESS;
2220  }
2221 
2222  for (tcpchan = 0; tcpchan < MAX_ASYNC_TCPCHAN; tcpchan++) {
2223  yEnterCriticalSection(&hub->ws.chan[tcpchan].access);
2224  if (hub->ws.chan[tcpchan].requests) {
2225  RequestSt *req = hub->ws.chan[tcpchan].requests;
2226  while (req) {
2227  while (req && req->ws.requestsize == req->ws.requestpos && (req->state == REQ_CLOSED || req->state == REQ_CLOSED_BY_API)) {
2228  req = req->ws.next;
2229  }
2230  if (req) {
2231  int throttle_start = req->ws.requestpos;
2232  int throttle_end = req->ws.requestsize;
2233  if (throttle_end > 2108 && hub->ws.remoteVersion >= USB_META_WS_PROTO_V2 && tcpchan == 0) {
2234  // Perform throttling on large uploads
2235  if (req->ws.requestpos == 0) {
2236  // First chunk is always first multiple of full window (124 bytes) above 2KB
2237  throttle_end = 2108;
2238  // Prepare to compute effective transfer rate
2239  hub->ws.chan[tcpchan].lastUploadAckBytes = 0;
2240  hub->ws.chan[tcpchan].lastUploadAckTime = 0;
2241  // Start with initial RTT based estimate
2242  hub->ws.uploadRate = hub->ws.tcpMaxWindowSize * 1000 / hub->ws.tcpRoundTripTime;
2243  } else if (hub->ws.chan[tcpchan].lastUploadAckTime == 0) {
2244  // first block not yet acked, wait more
2245  //WSLOG("wait for first ack");
2246  throttle_end = 0;
2247  } else {
2248  // adapt window frame to available bandwidth
2249  int bytesOnTheAir = req->ws.requestpos - hub->ws.chan[tcpchan].lastUploadAckBytes;
2250  u32 uploadRate = hub->ws.uploadRate;
2251  u64 timeOnTheAir = (yapiGetTickCount() - hub->ws.chan[tcpchan].lastUploadAckTime);
2252  u64 toBeSent = 2 * uploadRate + 1024 - bytesOnTheAir + (uploadRate * timeOnTheAir / 1000);
2253  if (toBeSent + bytesOnTheAir > DEFAULT_TCP_MAX_WINDOW_SIZE) {
2254  toBeSent = DEFAULT_TCP_MAX_WINDOW_SIZE - bytesOnTheAir;
2255  }
2256  WSLOG("throttling: %d bytes/s (%"FMTu64" + %d = %"FMTu64")\n", hub->ws.uploadRate, toBeSent, bytesOnTheAir, bytesOnTheAir + toBeSent);
2257  if (toBeSent < 64) {
2258  u64 waitTime = 1000 * (128 - toBeSent) / hub->ws.uploadRate;
2259  if (waitTime < 2) waitTime = 2;
2260  hub->ws.next_transmit_tm = yapiGetTickCount() + waitTime;
2261  WSLOG("WS: %d sent %"FMTu64"ms ago, waiting %"FMTu64"ms...\n", bytesOnTheAir, timeOnTheAir, waitTime);
2262  throttle_end = 0;
2263  }
2264  if (throttle_end > req->ws.requestpos + toBeSent) {
2265  // when sending partial content, round up to full frames
2266  if (toBeSent > 124) {
2267  toBeSent = (toBeSent / 124) * 124;
2268  }
2269  throttle_end = req->ws.requestpos + (u32)toBeSent;
2270  }
2271  }
2272  }
2273  while (req->ws.requestpos < throttle_end) {
2274  int stream = YSTREAM_TCP;
2275  int datalen = throttle_end - req->ws.requestpos;
2276  if (datalen > WS_MAX_DATA_LEN) {
2277  datalen = WS_MAX_DATA_LEN;
2278  }
2279  if (req->ws.requestpos == 0) {
2281  }
2282 
2283  if (req->ws.asyncId && (req->ws.requestpos + datalen == req->ws.requestsize)) {
2284  // last frame of an async request
2285  u8 tmp_data[128];
2286 
2287  if (datalen == WS_MAX_DATA_LEN) {
2288  // last frame is already full we must send the async close in another one
2289  res = ws_sendFrame(hub, stream, tcpchan, req->ws.requestbuf + req->ws.requestpos, datalen, errmsg);
2290  if (YISERR(res)) {
2291  req->errcode = res;
2292  YSTRCPY(req->errmsg, YOCTO_ERRMSG_LEN, errmsg);
2293  yLeaveCriticalSection(&hub->ws.chan[tcpchan].access);
2294  ySetEvent(&req->finished);
2295  return res;
2296  }
2297  WSLOG("ws_req:%p: send %d bytes on chan%d (%d/%d)\n", req, datalen, tcpchan, req->ws.requestpos, req->ws.requestsize);
2298  req->ws.requestpos += datalen;
2299  datalen = 0;
2300  }
2301  stream = YSTREAM_TCP_ASYNCCLOSE;
2302  if (datalen) {
2303  memcpy(tmp_data, req->ws.requestbuf + req->ws.requestpos, datalen);
2304  }
2305  tmp_data[datalen] = req->ws.asyncId;
2306  res = ws_sendFrame(hub, stream, tcpchan, tmp_data, datalen + 1, errmsg);
2307  WSLOG("req(%s:%p) sent async close %d\n", req->hub->name, req, req->ws.asyncId);
2309  } else {
2310  res = ws_sendFrame(hub, stream, tcpchan, req->ws.requestbuf + req->ws.requestpos, datalen, errmsg);
2312  //WSLOG("ws_req:%p: sent %d bytes on chan%d (%d/%d)\n", req, datalen, tcpchan, req->ws.requestpos, req->ws.requestsize);
2313  }
2314  if (YISERR(res)) {
2315  req->errcode = res;
2316  YSTRCPY(req->errmsg, YOCTO_ERRMSG_LEN, errmsg);
2317  yLeaveCriticalSection(&hub->ws.chan[tcpchan].access);
2318  ySetEvent(&req->finished);
2319  return res;
2320  }
2321  req->ws.requestpos += datalen;
2322  }
2323  if (req->ws.requestpos < req->ws.requestsize) {
2324  int sent = req->ws.requestpos - throttle_start;
2325  // not completely sent, cannot do more for now
2326  if (sent && hub->ws.uploadRate > 0) {
2327  u64 waitTime = 1000 * sent / hub->ws.uploadRate;
2328  if (waitTime < 2) waitTime = 2;
2329  hub->ws.next_transmit_tm = yapiGetTickCount() + waitTime;
2330  WSLOG("Sent %dbytes, waiting %"FMTu64"ms...\n", sent, waitTime);
2331  } else {
2332  hub->ws.next_transmit_tm = yapiGetTickCount() + 100;
2333  }
2334  req = NULL;
2335  } else {
2336  // end of request get ne following one
2337  req = req->ws.next;
2338  }
2339  }
2340  }
2341  }
2342  yLeaveCriticalSection(&hub->ws.chan[tcpchan].access);
2343 
2344  }
2345  return YAPI_SUCCESS;
2346 }
2347 
2348 
2349 
2350 /*
2351 * Open Base tcp socket (done in background by yws_thread)
2352 */
2353 static int ws_openBaseSocket(struct _WSNetHubSt *hub, yUrlRef url, const char *request, int request_len, int mstimout, char *errmsg)
2354 {
2355  char buffer[YOCTO_HOSTNAME_NAME];
2356  u32 ip;
2357  u16 port;
2359  yStrRef user, pass;
2360  int res, tcpchan;
2361 
2362  memset(hub, 0, sizeof(WSNetHub));
2363  hub->skt = INVALID_SOCKET;
2364  hub->s_next_async_id = 48;
2365 
2366  switch (yHashGetUrlPort(url, buffer, &port, &proto, &user, &pass)) {
2367  case NAME_URL:
2368  ip = resolveDNSCache(url, errmsg);
2369  if (ip == 0) {
2370  return YAPI_IO_ERROR;
2371  }
2372  break;
2373  case IP_URL:
2374  ip = inet_addr(buffer);
2375  break;
2376  default:
2377  return YERRMSG(YAPI_IO_ERROR, "not an IP hub");
2378  }
2379  if (proto == PROTO_HTTP) {
2380  return YERRMSG(YAPI_IO_ERROR, "not a websocket url");
2381  }
2382 
2383  res = yTcpOpen(&hub->skt, ip, port, mstimout, errmsg);
2384  if (YISERR(res)) {
2385  // yTcpOpen has reset the socket to INVALID
2386  yTcpClose(hub->skt);
2387  hub->skt = INVALID_SOCKET;
2388  return res;
2389  }
2390  hub->bws_open_tm = yapiGetTickCount();
2391  hub->bws_timeout_tm = mstimout;
2392  hub->user = user;
2393  hub->pass = pass;
2394  //write header
2395 
2396 
2397  res = yTcpWrite(hub->skt, request, request_len, errmsg);
2398  if (YISERR(res)) {
2399  yTcpClose(hub->skt);
2400  hub->skt = INVALID_SOCKET;
2401  return res;
2402  }
2403  res = yTcpWrite(hub->skt, ws_header_start, YSTRLEN(ws_header_start), errmsg);
2404  if (YISERR(res)) {
2405  yTcpClose(hub->skt);
2406  hub->skt = INVALID_SOCKET;
2407  return res;
2408  }
2409 
2410  hub->websocket_key_len = GenereateWebSockeyKey((u8*)request, request_len, hub->websocket_key);
2411  res = yTcpWrite(hub->skt, hub->websocket_key, hub->websocket_key_len, errmsg);
2412  if (YISERR(res)) {
2413  yTcpClose(hub->skt);
2414  hub->skt = INVALID_SOCKET;
2415  return res;
2416  }
2417 
2418  res = yTcpWrite(hub->skt, ws_header_end, YSTRLEN(ws_header_end), errmsg);
2419  if (YISERR(res)) {
2420  yTcpClose(hub->skt);
2421  hub->skt = INVALID_SOCKET;
2422  return res;
2423  }
2424 
2425  hub->fifo_buffer = yMalloc(2048);
2426  yFifoInit(&hub->mainfifo, hub->fifo_buffer, 2048);
2427  for (tcpchan = 0; tcpchan < MAX_ASYNC_TCPCHAN; tcpchan++) {
2428  yInitializeCriticalSection(&hub->chan[tcpchan].access);
2429  }
2430  return YAPI_SUCCESS;
2431 }
2432 
2433 
2434 
2435 
2436 /*
2437 * Close Base tcp socket (done in background by yws_thread)
2438 */
2439 static void ws_closeBaseSocket(struct _WSNetHubSt *base_req)
2440 {
2441  int tcpchan;
2442  yTcpClose(base_req->skt);
2443  base_req->skt = INVALID_SOCKET;
2444  for (tcpchan = 0; tcpchan < MAX_ASYNC_TCPCHAN; tcpchan++) {
2445  yDeleteCriticalSection(&base_req->chan[tcpchan].access);
2446  }
2447  yFifoCleanup(&base_req->mainfifo);
2448  yFree(base_req->fifo_buffer);
2449 }
2450 
2451 
2452 /*
2453 * select used by background thread
2454 */
2455 static int ws_thread_select(struct _WSNetHubSt *base_req, u64 ms, WakeUpSocket *wuce, char *errmsg)
2456 {
2457  fd_set fds;
2458  struct timeval timeout;
2459  int res;
2460  YSOCKET sktmax = 0;
2461 
2462  memset(&timeout, 0, sizeof(timeout));
2463  timeout.tv_sec = (long)ms / 1000;
2464  timeout.tv_usec = (int)(ms % 1000) * 1000;
2465  /* wait for data */
2466  FD_ZERO(&fds);
2467  if (wuce) {
2468  FD_SET(wuce->listensock, &fds);
2469  sktmax = wuce->listensock;
2470  }
2471 
2472  if (base_req->skt == INVALID_SOCKET) {
2473  return YERR(YAPI_INVALID_ARGUMENT);
2474  } else {
2475  FD_SET(base_req->skt, &fds);
2476  if (base_req->skt > sktmax)
2477  sktmax = base_req->skt;
2478  }
2479  if (sktmax == 0) {
2480  return YAPI_SUCCESS;
2481  }
2482  res = select((int)sktmax + 1, &fds, NULL, NULL, &timeout);
2483  if (res < 0) {
2484 #ifndef WINDOWS_API
2485  if (SOCK_ERR == EAGAIN) {
2486  return 0;
2487  } else
2488 #endif
2489  {
2490  res = yNetSetErr();
2491  return res;
2492  }
2493  }
2494  if (res != 0) {
2495  if (wuce && FD_ISSET(wuce->listensock, &fds)) {
2496  int signal = yConsumeWakeUpSocket(wuce, errmsg);
2497  //dbglog("exit from sleep with WUCE (%d)\n", signal);
2498  YPROPERR(signal);
2499  }
2500  if (FD_ISSET(base_req->skt, &fds)) {
2501  int avail = yFifoGetFree(&base_req->mainfifo);
2502  int readed = 0;
2503  if (avail) {
2504  u8 buffer[2048];
2505  if (avail > 2048) {
2506  avail = 2048;
2507  }
2508  readed = yTcpRead(base_req->skt, buffer, avail, errmsg);
2509  if (readed > 0) {
2510  yPushFifo(&base_req->mainfifo, buffer, readed);
2511  }
2512  }
2513  return readed;
2514  }
2515  }
2516  return YAPI_SUCCESS;
2517 }
2518 
2519 
2520 
2522 {
2523  hub->attemptDelay = 500 << hub->retryCount;
2524  if (hub->attemptDelay > 8000)
2525  hub->attemptDelay = 8000;
2526  hub->retryCount++;
2527 #ifdef DEBUG_WEBSOCKET
2528  dbglog("hub(%s): IO error on ws_thread:(%d) %s\n", hub->name, hub->errcode, hub->errmsg);
2529  dbglog("hub(%s): retry in %dms (%d retries)\n", hub->name, hub->attemptDelay, hub->retryCount);
2530 #endif
2531 
2532 }
2533 
2537 void* ws_thread(void* ctx)
2538 {
2539  char *p;
2540  yThread *thread = (yThread*)ctx;
2541  char errmsg[YOCTO_ERRMSG_LEN];
2542  HubSt *hub = (HubSt*)thread->ctx;
2543  int res;
2544  int first_notification_connection = 1;
2545  u8 header[8];
2546  char buffer[2048];
2547  int buffer_ofs = 0;
2548  int continue_processing;
2549 
2550 
2551  yThreadSignalStart(thread);
2552  WSLOG("hub(%s) start thread \n", hub->name);
2553 
2554  while (!yThreadMustEnd(thread) && hub->state != NET_HUB_TOCLOSE) {
2555  char request[256];
2556 
2557  if (hub->retryCount > 0) {
2558  u64 timeout = yapiGetTickCount() + hub->attemptDelay;
2559  do {
2560  //minimal timouout is allways 500
2561  yApproximateSleep(100);
2562  } while (timeout > yapiGetTickCount());
2563  }
2564  if (hub->state == NET_HUB_TOCLOSE) {
2565  break;
2566  }
2567  WSLOG("hub(%s) try to open WS connection at %d\n", hub->name, hub->notifAbsPos);
2568  if (first_notification_connection) {
2569  YSPRINTF(request, 256, "GET /not.byn");
2570  } else {
2571  YSPRINTF(request, 256, "GET /not.byn?abs=%u", hub->notifAbsPos);
2572  }
2573  res = ws_openBaseSocket(&hub->ws, hub->url, request, YSTRLEN(request), 1000, errmsg);
2574  hub->lastAttempt = yapiGetTickCount();
2575  if (YISERR(res)) {
2577  hub->errcode = ySetErr(res, hub->errmsg, errmsg, NULL, 0);
2580  continue;
2581  }
2582  WSLOG("hub(%s) base socket opened (skt=%x)\n", hub->name, hub->ws.skt);
2583  hub->state = NET_HUB_TRYING;
2585  hub->ws.connectionTime = 0;
2588  errmsg[0] = 0;
2589  continue_processing = 1;
2590  do {
2591  u64 wait;
2592  u64 now = yapiGetTickCount();
2593  if (hub->ws.next_transmit_tm >= now) {
2594  wait = hub->ws.next_transmit_tm - now;
2595  } else {
2596  wait = 1000;
2597  }
2598  //dbglog("select %"FMTu64"ms on main socket\n", wait);
2599  res = ws_thread_select(&hub->ws, wait, &hub->wuce, errmsg);
2600  if (YISERR(res)) {
2601  WSLOG("hub(%s) ws_thread_select error %d:%s\n", hub->name, res, errmsg);
2602  }
2603 
2604  if (res > 0) {
2605  int need_more_data = 0;
2606  int avail, rw;
2607  int hdrlen;
2608  u32 mask;
2609  int websocket_ok = 0;
2610  int pktlen;
2611  do {
2612  u16 pos;
2613  //something to handle;
2614  switch (hub->ws.base_state) {
2615  case WS_BASE_HEADER_SENT:
2616  pos = ySeekFifo(&hub->ws.mainfifo, (const u8*)"\r\n\r\n", 4, 0, 0, 0);
2617  if (pos == 0xffff) {
2618  if ((u64)(yapiGetTickCount() - hub->lastAttempt) > WS_CONNEXION_TIMEOUT) {
2619  res = YERR(YAPI_TIMEOUT);
2620  } else {
2621  need_more_data = 1;
2622  }
2623  break;
2624  } else if (pos >= 2044) {
2625  res = YERRMSG(YAPI_IO_ERROR, "Bad reply header");
2626  // fatal error do not retry to reconnect
2627  hub->state = NET_HUB_TOCLOSE;
2628  break;
2629  }
2630  pos = ySeekFifo(&hub->ws.mainfifo, (const u8*)"\r\n", 2, 0, 0, 0);
2631  yPopFifo(&hub->ws.mainfifo, (u8*)buffer, pos + 2);
2632  if (YSTRNCMP(buffer, "HTTP/1.1 ", 9) != 0) {
2633  res = YERRMSG(YAPI_IO_ERROR, "Bad reply header");
2634  // fatal error do not retry to reconnect
2635  hub->state = NET_HUB_TOCLOSE;
2636  break;
2637  }
2638  p = buffer + 9;
2639  if (YSTRNCMP(p, "101", 3) != 0) {
2640  res = YERRMSG(YAPI_IO_ERROR, "hub does not support WebSocket");
2641  // fatal error do not retry to reconnect
2642  hub->state = NET_HUB_TOCLOSE;
2643  break;
2644  }
2645  websocket_ok = 0;
2646  pos = ySeekFifo(&hub->ws.mainfifo, (const u8*)"\r\n", 2, 0, 0, 0);
2647  while (pos != 0) {
2648  yPopFifo(&hub->ws.mainfifo, (u8*)buffer, pos + 2);
2649  if (pos > 22 && YSTRNICMP(buffer, "Sec-WebSocket-Accept: ", 22) == 0) {
2650  if (!VerifyWebsocketKey(buffer + 22, pos, hub->ws.websocket_key, hub->ws.websocket_key_len)) {
2651  websocket_ok = 1;
2652  } else {
2653  res = YERRMSG(YAPI_IO_ERROR, "hub does not use same WebSocket protocol");
2654  // fatal error do not retry to reconnect
2655  hub->state = NET_HUB_TOCLOSE;
2656  break;
2657  }
2658  }
2659  if ((u64)(yapiGetTickCount() - hub->lastAttempt) > WS_CONNEXION_TIMEOUT) {
2660  res = YERR(YAPI_TIMEOUT);
2661  break;
2662  }
2663  pos = ySeekFifo(&hub->ws.mainfifo, (const u8*)"\r\n", 2, 0, 0, 0);
2664  }
2665  yPopFifo(&hub->ws.mainfifo, NULL, 2);
2666  if (websocket_ok) {
2668  buffer_ofs = 0;
2669  } else {
2670  res = YERRMSG(YAPI_IO_ERROR, "Invalid WebSocket header");
2671  // fatal error do not retry to reconnect
2672  hub->state = NET_HUB_TOCLOSE;
2673  }
2674  break;
2677  case WS_BASE_CONNECTED:
2678 
2679  avail = yFifoGetUsed(&hub->ws.mainfifo);
2680  if (avail < 2) {
2681  need_more_data = 1;
2682  break;
2683  }
2684  rw = (avail < 7 ? avail : 7);
2685  yPeekFifo(&hub->ws.mainfifo, header, rw, 0);
2686  pktlen = header[1] & 0x7f;
2687  if (pktlen > 125) {
2688  // Unsupported long frame, drop all incoming data (probably 1+ frame(s))
2689  res = YERRMSG(YAPI_IO_ERROR, "Unsupported long websocket frame");
2690  break;
2691  }
2692 
2693  if (header[1] & 0x80) {
2694  // masked frame
2695  hdrlen = 6;
2696  if (avail < hdrlen + pktlen) {
2697  need_more_data = 1;
2698  break;
2699  }
2700  memcpy(&mask, header + 2, sizeof(u32));
2701  } else {
2702  // plain frame
2703  hdrlen = 2;
2704  if (avail < hdrlen + pktlen) {
2705  need_more_data = 1;
2706  break;
2707  }
2708  mask = 0;
2709  }
2710 
2711  if ((header[0] & 0x7f) != 0x02) {
2712  // Non-data frame
2713  if (header[0] == 0x88) {
2714  //if (USBTCPIsPutReady(sock) < 8) return;
2715  // websocket close, reply with a close
2716  header[0] = 0x88;
2717  header[1] = 0x82;
2718  mask = YRand32();
2719  memcpy(header + 2, &mask, sizeof(u32));
2720  header[6] = 0x03 ^ ((u8 *)&mask)[0];
2721  header[7] = 0xe8 ^ ((u8 *)&mask)[1];
2722  res = yTcpWrite(hub->ws.skt, (char*)header, 8, errmsg);
2723  if (YISERR(res)) {
2724  break;
2725  }
2726  hub->ws.base_state = WS_BASE_OFFLINE;
2727 #ifdef DEBUG_WEBSOCKET
2728  dbglog("WS: io error on base socket of %s(%X): %s\n", hub->name, hub->url, errmsg);
2729 #endif
2730  } else {
2731  // unhandled packet
2732  dbglog("unhandled packet:%x%x\n", header[0], header[1]);
2733  }
2734  yPopFifo(&hub->ws.mainfifo, NULL, hdrlen + pktlen);
2735  break;
2736  }
2737  // drop frame header
2738  yPopFifo(&hub->ws.mainfifo, NULL, hdrlen);
2739  // append
2740  yPopFifo(&hub->ws.mainfifo, (u8*)buffer + buffer_ofs, pktlen);
2741  if (mask) {
2742  int i;
2743  for (i = 0; i < (pktlen + 1 + 3) >> 2; i++) {
2744  buffer[buffer_ofs + i] ^= mask;
2745  }
2746  }
2747 
2748  if (header[0] == 0x02) {
2749  // fragmented binary frame
2750  WSStreamHead strym;
2751  strym.encaps = buffer[buffer_ofs];
2752  if (strym.stream == YSTREAM_META) {
2753  // unsupported fragmented META stream, should never happen
2754  dbglog("Warning:fragmented META\n");
2755  break;
2756  }
2757  buffer_ofs += pktlen;
2758  break;
2759  }
2760 
2761  res = ws_parseIncommingFrame(hub, (u8*)buffer, buffer_ofs + pktlen, errmsg);
2762  if (YISERR(res)) {
2763  WSLOG("hub(%s) ws_parseIncommingFrame error %d:%s\n", hub->name, res, errmsg);
2764  break;
2765  }
2766  buffer_ofs = 0;
2767  break;
2768  case WS_BASE_OFFLINE:
2769  break;
2770  }
2771  } while (!need_more_data && !YISERR(res));
2772  }
2773  if (!YISERR(res)) {
2774  res = ws_processRequests(hub, errmsg);
2775  if (YISERR(res)) {
2776  WSLOG("hub(%s) ws_processRequests error %d:%s\n", hub->name, res, errmsg);
2777  }
2778  }
2779 
2780  if (YISERR(res)) {
2781  continue_processing = 0;
2782  } else if ((yThreadMustEnd(thread) || hub->state == NET_HUB_TOCLOSE) && !ws_requestStillPending(hub)) {
2783  continue_processing = 0;
2784  }
2785  } while (continue_processing);
2786  if (YISERR(res)) {
2787  WSLOG("hub(%s) io error %d:%s\n", hub->name,res, errmsg);
2789  hub->errcode = ySetErr(res, hub->errmsg, errmsg, NULL, 0);
2792  }
2793  WSLOG("hub(%s) close base socket %d:%s\n", hub->name, res, errmsg);
2794  ws_closeBaseSocket(&hub->ws);
2795  if (hub->state != NET_HUB_TOCLOSE) {
2796  hub->state = NET_HUB_DISCONNECTED;
2797  }
2798  }
2799  WSLOG("hub(%s) exit thread \n", hub->name);
2800  hub->state = NET_HUB_CLOSED;
2801  yThreadSignalEnd(thread);
2802  return NULL;
2803 }
2804 
2805 
2806 
2807 /********************************************************************************
2808  * UDP funtions
2809  *******************************************************************************/
2810 
2811 //#define DEBUG_NET_DETECTION
2812 
2815 
2816 
2817 #ifdef WINDOWS_API
2818 YSTATIC int yDetectNetworkInterfaces(u32 only_ip)
2819 {
2820  INTERFACE_INFO winIfaces[NB_OS_IFACES];
2821  DWORD returnedSize, nbifaces, i;
2822  SOCKET sock;
2823 
2824  nbDetectedIfaces = 0;
2825  memset(detectedIfaces, 0, sizeof(detectedIfaces));
2826  sock = WSASocket(AF_INET, SOCK_DGRAM, 0, 0, 0, 0);
2827  if (sock == INVALID_SOCKET){
2828  yNetLogErr();
2829  return -1;
2830  }
2831  if (WSAIoctl(sock, SIO_GET_INTERFACE_LIST, NULL, 0, winIfaces, sizeof(winIfaces), &returnedSize, NULL, NULL)<0){
2832  yNetLogErr();
2833  return -1;
2834  }
2835 
2836  nbifaces = returnedSize / sizeof(INTERFACE_INFO);
2837  for (i = 0; i<nbifaces; i++){
2838  if (winIfaces[i].iiFlags & IFF_LOOPBACK)
2839  continue;
2840  if (winIfaces[i].iiFlags & IFF_UP){
2841  if (winIfaces[i].iiFlags & IFF_MULTICAST)
2842  detectedIfaces[nbDetectedIfaces].flags |= OS_IFACE_CAN_MCAST;
2843  if (only_ip != 0 && only_ip != winIfaces[i].iiAddress.AddressIn.sin_addr.S_un.S_addr){
2844  continue;
2845  }
2846  detectedIfaces[nbDetectedIfaces].ip = winIfaces[i].iiAddress.AddressIn.sin_addr.S_un.S_addr;
2847  detectedIfaces[nbDetectedIfaces].netmask = winIfaces[i].iiNetmask.AddressIn.sin_addr.S_un.S_addr;
2848  nbDetectedIfaces++;
2849  }
2850  }
2851  return nbDetectedIfaces;
2852 
2853 }
2854 #else
2855 
2856 #include <net/if.h>
2857 #include <ifaddrs.h>
2859 {
2860  struct ifaddrs *if_addrs = NULL;
2861  struct ifaddrs *p = NULL;
2862 #if 1
2863  nbDetectedIfaces = 0;
2864  memset(detectedIfaces, 0, sizeof(detectedIfaces));
2865  if (getifaddrs(&if_addrs) != 0){
2866  yNetLogErr();
2867  return -1;
2868  }
2869  p = if_addrs;
2870  while (p) {
2871  if (p->ifa_addr && p->ifa_addr->sa_family == AF_INET) {
2872  struct sockaddr_in *tmp;
2873  u32 ip, netmask;
2874  tmp = (struct sockaddr_in*)p->ifa_addr;
2875  ip = tmp->sin_addr.s_addr;
2876  if (only_ip != 0 && only_ip != ip){
2877  p = p->ifa_next;
2878  continue;
2879  }
2880  tmp = (struct sockaddr_in*)p->ifa_netmask;
2881  netmask = tmp->sin_addr.s_addr;
2882  if ((p->ifa_flags & IFF_LOOPBACK) == 0){
2883  if (p->ifa_flags & IFF_UP && p->ifa_flags & IFF_RUNNING){
2884 #ifdef DEBUG_NET_DETECTION
2885  ylogf("%s : ", p->ifa_name);
2886  ylogIP(ip);
2887  ylogf("/");
2888  ylogIP(netmask);
2889  ylogf(" (%X)\n", p->ifa_flags);
2890 #endif
2891  if (p->ifa_flags & IFF_MULTICAST){
2892  detectedIfaces[nbDetectedIfaces].flags |= OS_IFACE_CAN_MCAST;
2893  }
2894  detectedIfaces[nbDetectedIfaces].ip = ip;
2895  detectedIfaces[nbDetectedIfaces].netmask = netmask;
2896  nbDetectedIfaces++;
2897  }
2898  }
2899 #ifdef DEBUG_NET_DETECTION
2900  else {
2901  ylogf("drop %s : ", p->ifa_name);
2902  ylogIP(ip);
2903  ylogf("/");
2904  ylogIP(netmask);
2905  ylogf(" (%X)\n", p->ifa_flags);
2906  }
2907 #endif
2908  }
2909  p = p->ifa_next;
2910  }
2911 
2912 #else
2913  nbDetectedIfaces = 1;
2914  memset(detectedIfaces, 0, sizeof(detectedIfaces));
2915  detectedIfaces[0].flags |= OS_IFACE_CAN_MCAST;
2916  detectedIfaces[0].ip = INADDR_ANY;
2917 #endif
2918  return nbDetectedIfaces;
2919 }
2920 
2921 #endif
2922 
2923 
2924 
2925 
2926 static const char *discovery =
2927  "M-SEARCH * HTTP/1.1\r\n"
2928  "HOST:" YSSDP_MCAST_ADDR_STR ":" TOSTRING(YSSDP_PORT) "\r\n"
2929  "MAN:\"ssdp:discover\"\r\n"
2930  "MX:5\r\n"
2931  "ST:" YSSDP_URN_YOCTOPUCE"\r\n"
2932  "\r\n";
2933 
2934 
2935 #define SSDP_NOTIFY "NOTIFY * HTTP/1.1\r\n"
2936 #define SSDP_M_SEARCH "M-SEARCH * HTTP/1.1\r\n"
2937 #define SSDP_HTTP "HTTP/1.1 200 OK\r\n"
2938 #define SSDP_LINE_MAX_LEN 80u
2939 
2940 #define UDP_IN_FIFO yFifoBuf
2941 
2942 
2943 static char hexatochar(char hi_c, char lo_c)
2944 {
2945  u8 hi, lo;
2946  hi = ((u8)(hi_c)& 0x1f) ^ 0x10;
2947  lo = ((u8)(lo_c) & 0x1f) ^ 0x10;
2948  if (hi & 0x10) hi -= 7;
2949  if (lo & 0x10) lo -= 7;
2950  return (hi << 4) + lo;
2951 }
2952 
2953 static int uuidToSerial(const char * uuid, char *serial)
2954 {
2955  int i;
2956  int len, padlen;
2957  char *s = serial;
2958  const char *u = uuid;
2959 
2960  for (i = 0, u = uuid; i < 4; i++, u += 2){
2961  *s++ = hexatochar(*u, *(u + 1));
2962  }
2963  u++;
2964  for (; i< 6; i++, u += 2){
2965  *s++ = hexatochar(*u, *(u + 1));
2966  }
2967  u++;
2968  for (; i< 8; i++, u += 2){
2969  *s++ = hexatochar(*u, *(u + 1));
2970  }
2971  *s++ ='-';
2972  u = strstr(uuid, "-COFF-EE");
2973  if (u == NULL) {
2974  return -1;
2975  }
2976  u += 8;
2977  while (*u == '0') u++;
2978  len = YSTRLEN(u);
2979  if (YSTRNCMP(serial, "VIRTHUB0", YOCTO_BASE_SERIAL_LEN) == 0) {
2980  padlen = YOCTO_SERIAL_SEED_SIZE - 1;
2981  } else {
2982  padlen = 5;
2983  }
2984  for (i = len; i < padlen; i++) {
2985  *s++ = '0';
2986  }
2987  *s = 0;
2988  YSTRCAT(serial, YOCTO_SERIAL_LEN, u);
2989  return 0;
2990 }
2991 
2992 
2993 static void ySSDPUpdateCache(SSDPInfos *SSDP, const char *uuid, const char * url, int cacheValidity)
2994 {
2995  int i;
2996 
2997  if(cacheValidity<=0)
2998  cacheValidity = 1800;
2999  cacheValidity*=1000;
3000 
3001  for (i = 0; i < NB_SSDP_CACHE_ENTRY; i++){
3002  SSDP_CACHE_ENTRY *p = SSDP->SSDPCache[i];
3003  if (p == NULL)
3004  break;
3005  if (YSTRCMP(uuid,p->uuid) == 0) {
3007  p->maxAge = cacheValidity;
3008 
3009  if (YSTRCMP(url, p->url)){
3010  if (SSDP->callback) {
3011  SSDP->callback(p->serial, url, p->url);
3012  }
3013  YSTRCPY(p->url, SSDP_URL_LEN, url);
3014  } else {
3015  if (SSDP->callback){
3016  SSDP->callback(p->serial, url, NULL);
3017  }
3018  }
3019  return;
3020  }
3021  }
3022  if (i < NB_SSDP_CACHE_ENTRY){
3024  YSTRCPY(p->uuid, SSDP_URL_LEN, uuid);
3025  if (uuidToSerial(p->uuid, p->serial) < 0) {
3026  yFree(p);
3027  return;
3028  }
3029  YSTRCPY(p->url,SSDP_URL_LEN,url);
3031  p->maxAge = cacheValidity;
3032  SSDP->SSDPCache[i] = p;
3033  if (SSDP->callback){
3034  SSDP->callback(p->serial, p->url, NULL);
3035  }
3036  }
3037 }
3038 
3040 {
3041  int i;
3042  u64 now =yapiGetTickCount();
3043 
3044  for (i = 0; i<NB_SSDP_CACHE_ENTRY; i++) {
3045  SSDP_CACHE_ENTRY *p = SSDP->SSDPCache[i];
3046  if (p == NULL)
3047  break;
3048  if ((u64) (now - p->detectedTime) > p->maxAge) {
3049  p->maxAge = 0;
3050  if (SSDP->callback) {
3051  SSDP->callback(p->serial, NULL, p->url);
3052  }
3053  }
3054  }
3055 }
3056 
3057 
3058 
3059 
3060 static void ySSDP_parseSSPDMessage(SSDPInfos *SSDP, char *message,int msg_len)
3061 {
3062  int len =0;
3063  char *p,*start,*lastsep;
3064  char *location=NULL;
3065  char *usn=NULL;
3066  char *cache=NULL;
3067 
3068  if(len>=msg_len){
3069  return;
3070  }
3071 
3072  if (memcmp(message,SSDP_HTTP,YSTRLEN(SSDP_HTTP))==0) {
3073  len=YSTRLEN(SSDP_HTTP);
3074  } else if (memcmp(message,SSDP_NOTIFY,YSTRLEN(SSDP_NOTIFY))==0) {
3075  len=YSTRLEN(SSDP_NOTIFY);
3076  }
3077  if (len){
3078  //dbglog("SSDP Message:\n%s\n",message);
3079  start = p = lastsep= message +len;
3080  msg_len-=len;
3081  while( msg_len && *p ){
3082  switch(*p) {
3083  case ':':
3084  if (lastsep == start){
3085  lastsep = p;
3086  }
3087  break;
3088  case '\r':
3089  if (p==start){
3090  // \r\n\r\n ->end
3091  if(msg_len>1) msg_len=1;
3092  break;
3093  }
3094 
3095  if (lastsep == start){
3096  //no : on the line -> drop this message
3097  return;
3098  }
3099  *lastsep++=0;
3100  if (*lastsep==' ') lastsep++;
3101  *p=0;
3102  if (strcmp(start,"LOCATION")==0){
3103  location=lastsep;
3104  }else if (strcmp(start,"USN")==0){
3105  usn=lastsep;
3106  }else if (strcmp(start,"CACHE-CONTROL")==0){
3107  cache=lastsep;
3108  }
3109  break;
3110  case '\n':
3111  start =lastsep= p+1;
3112  break;
3113  }
3114  p++;
3115  msg_len--;
3116  }
3117  if(location && usn && cache){
3118  const char *uuid,*urn;
3119  int cacheVal;
3120  //dbglog("SSDP: location: %s %s %s\n\n",location,usn,cache);
3121  // parse USN
3122  p=usn;
3123  // ReSharper disable once CppPossiblyErroneousEmptyStatements
3124  while (*p && *p++!=':');
3125  if (!*p) return;
3126  uuid=p;
3127  // ReSharper disable once CppPossiblyErroneousEmptyStatements
3128  while (*p && *p++!=':');
3129  if (*p != ':') return;
3130  *(p++-1)=0;
3131  if (!*p) return;
3132  urn=p;
3133  // parse Location
3134  if(YSTRNCMP(location,"http://",7)==0){
3135  location += 7;
3136  }
3137  p=location;
3138  while (*p && *p != '/') p++;
3139  if(*p=='/') *p=0;
3140  p=cache;
3141  // ReSharper disable once CppPossiblyErroneousEmptyStatements
3142  while (*p && *p++!='=');
3143  if(!*p) return;
3144  cacheVal = atoi(p);
3145  if (YSTRCMP(urn,YSSDP_URN_YOCTOPUCE)==0){
3146  ySSDPUpdateCache(SSDP, uuid, location, cacheVal);
3147  }
3148  }
3149  }
3150 #if 0
3151  else {
3152  dbglog("SSDP drop invalid message:\n%s\n",message);
3153  }
3154 #endif
3155 }
3156 
3157 
3158 
3159 static void* ySSDP_thread(void* ctx)
3160 {
3161  yThread *thread=(yThread*)ctx;
3162  SSDPInfos *SSDP = (SSDPInfos*)thread->ctx;
3163  fd_set fds;
3164  u8 buffer[1536];
3165  struct timeval timeout;
3166  int res, received, i;
3167  YSOCKET sktmax;
3168  yFifoBuf inFifo;
3169 
3170 
3171  yThreadSignalStart(thread);
3172  yFifoInit(&inFifo,buffer,sizeof(buffer));
3173 
3174  while (!yThreadMustEnd(thread)) {
3175  memset(&timeout,0,sizeof(timeout));
3176  timeout.tv_sec = (long)1;
3177  timeout.tv_usec = (int)0;
3178  /* wait for data */
3179  FD_ZERO(&fds);
3180  sktmax = 0;
3181  for (i = 0; i < nbDetectedIfaces; i++) {
3182  FD_SET(SSDP->request_sock[i], &fds);
3183  if (SSDP->request_sock[i] > sktmax) {
3184  sktmax = SSDP->request_sock[i];
3185  }
3186  if(SSDP->notify_sock[i] != INVALID_SOCKET) {
3187  FD_SET(SSDP->notify_sock[i], &fds);
3188  if (SSDP->notify_sock[i] > sktmax) {
3189  sktmax = SSDP->notify_sock[i];
3190  }
3191  }
3192  }
3193  res = select((int)sktmax + 1, &fds, NULL, NULL, &timeout);
3194  if (res<0) {
3195  #ifndef WINDOWS_API
3196  if(SOCK_ERR == EAGAIN){
3197  continue;
3198  } else
3199  #endif
3200  {
3201  yNetLogErr();
3202  break;
3203  }
3204  }
3205 
3206  if(!yContext) continue;
3207  ySSDPCheckExpiration(SSDP);
3208  if (res != 0) {
3209  for (i = 0; i < nbDetectedIfaces; i++) {
3210  if (FD_ISSET(SSDP->request_sock[i], &fds)) {
3211  received = (int)yrecv(SSDP->request_sock[i], (char*)buffer, sizeof(buffer)-1, 0);
3212  if (received>0) {
3213  buffer[received] = 0;
3214  ySSDP_parseSSPDMessage(SSDP, (char*)buffer, received);
3215  }
3216  }
3217  if (FD_ISSET(SSDP->notify_sock[i], &fds)) {
3218  received = (int)yrecv(SSDP->notify_sock[i], (char *)buffer, sizeof(buffer)-1, 0);
3219  if (received > 0) {
3220  buffer[received] = 0;
3221  ySSDP_parseSSPDMessage(SSDP, (char*)buffer, received);
3222  }
3223  }
3224  }
3225  }
3226  }
3227  yFifoCleanup(&inFifo);
3228  yThreadSignalEnd(thread);
3229  return NULL;
3230 }
3231 
3232 
3233 int ySSDPDiscover(SSDPInfos *SSDP, char *errmsg)
3234 {
3235  int sent, len, i;
3236  struct sockaddr_in sockaddr_dst;
3237 
3238  for (i = 0; i < nbDetectedIfaces; i++) {
3239  memset(&sockaddr_dst, 0, sizeof(struct sockaddr_in));
3240  sockaddr_dst.sin_family = AF_INET;
3241  sockaddr_dst.sin_port = htons(YSSDP_PORT);
3242  sockaddr_dst.sin_addr.s_addr = inet_addr(YSSDP_MCAST_ADDR_STR);
3243  len = (int)strlen(discovery);
3244  sent = (int)sendto(SSDP->request_sock[i], discovery, len, 0, (struct sockaddr *)&sockaddr_dst, sizeof(struct sockaddr_in));
3245  if (sent < 0) {
3246  return yNetSetErr();
3247  }
3248  }
3249  return YAPI_SUCCESS;
3250 }
3251 
3252 
3253 int ySSDPStart(SSDPInfos *SSDP, ssdpHubDiscoveryCallback callback, char *errmsg)
3254 {
3255  u32 optval;
3256  int i;
3257  socklen_t socksize;
3258  struct sockaddr_in sockaddr;
3259  struct ip_mreq mcast_membership;
3260 
3261  if (SSDP->started)
3262  return YAPI_SUCCESS;
3263 
3264  memset(SSDP, 0, sizeof(SSDPInfos));
3265  SSDP->callback = callback;
3267 
3268  for (i = 0; i < nbDetectedIfaces; i++) {
3269  //create M-search socker
3270  SSDP->request_sock[i] = ysocket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
3271  if (SSDP->request_sock[i] == INVALID_SOCKET) {
3272  return yNetSetErr();
3273  }
3274 
3275  optval = 1;
3276  setsockopt(SSDP->request_sock[i], SOL_SOCKET, SO_REUSEADDR, (char *)&optval, sizeof(optval));
3277 #ifdef SO_REUSEPORT
3278  setsockopt(SSDP->request_sock[i], SOL_SOCKET, SO_REUSEPORT, (char *)&optval, sizeof(optval));
3279 #endif
3280 
3281  // set port to 0 since we accept any port
3282  socksize = sizeof(sockaddr);
3283  memset(&sockaddr, 0, socksize);
3284  sockaddr.sin_family = AF_INET;
3285  sockaddr.sin_addr.s_addr = detectedIfaces[i].ip;
3286  if (bind(SSDP->request_sock[i], (struct sockaddr*) &sockaddr, socksize) < 0) {
3287  return yNetSetErr();
3288  }
3289  //create NOTIFY socker
3290  SSDP->notify_sock[i] = ysocket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
3291  if (SSDP->notify_sock[i] == INVALID_SOCKET) {
3292  return yNetSetErr();
3293  }
3294 
3295  optval = 1;
3296  setsockopt(SSDP->notify_sock[i], SOL_SOCKET, SO_REUSEADDR, (char *)&optval, sizeof(optval));
3297 #ifdef SO_REUSEPORT
3298  setsockopt(SSDP->notify_sock[i], SOL_SOCKET, SO_REUSEPORT, (char *)&optval, sizeof(optval));
3299 #endif
3300 
3301  // set port to 0 since we accept any port
3302  socksize = sizeof(sockaddr);
3303  memset(&sockaddr, 0, socksize);
3304  sockaddr.sin_family = AF_INET;
3305  sockaddr.sin_port = htons(YSSDP_PORT);
3306  sockaddr.sin_addr.s_addr = INADDR_ANY;
3307  if (bind(SSDP->notify_sock[i], (struct sockaddr *)&sockaddr, socksize) < 0) {
3308  return yNetSetErr();
3309  }
3310 
3311  mcast_membership.imr_multiaddr.s_addr = inet_addr(YSSDP_MCAST_ADDR_STR);
3312  mcast_membership.imr_interface.s_addr = INADDR_ANY;
3313  if (setsockopt(SSDP->notify_sock[i], IPPROTO_IP, IP_ADD_MEMBERSHIP, (void*)&mcast_membership, sizeof(mcast_membership)) < 0){
3314  dbglog("Unable to add multicat membership for SSDP");
3315  yNetLogErr();
3316  yclosesocket(SSDP->notify_sock[i]);
3317  SSDP->notify_sock[i] = INVALID_SOCKET;
3318  }
3319  }
3320  //yThreadCreate will not create a new thread if there is already one running
3321  if(yThreadCreate(&SSDP->thread,ySSDP_thread,SSDP)<0){
3322  return YERRMSG(YAPI_IO_ERROR,"Unable to start helper thread");
3323  }
3324  SSDP->started++;
3325  return ySSDPDiscover(SSDP,errmsg);
3326  //return YAPI_SUCCESS;
3327 }
3328 
3329 
3331 {
3332  int i;
3333 
3334  if(yThreadIsRunning(&SSDP->thread)) {
3335  u64 timeref;
3336  yThreadRequestEnd(&SSDP->thread);
3337  timeref = yapiGetTickCount();
3338  while(yThreadIsRunning(&SSDP->thread) && (yapiGetTickCount() - timeref < 1000) ) {
3339  yApproximateSleep(10);
3340  }
3341  yThreadKill(&SSDP->thread);
3342  }
3343 
3344  //unregister all detected hubs
3345  for (i = 0; i<NB_SSDP_CACHE_ENTRY; i++){
3346  SSDP_CACHE_ENTRY *p = SSDP->SSDPCache[i];
3347  if(p== NULL)
3348  continue;
3349  if (p->maxAge) {
3350  yapiUnregisterHub(p->url);
3351  p->maxAge=0;
3352  if (SSDP->callback)
3353  SSDP->callback(p->serial, NULL, p->url);
3354  }
3355  yFree(p);
3356  }
3357 
3358  for (i = 0; i < nbDetectedIfaces; i++) {
3359 
3360  if (SSDP->request_sock[i] != INVALID_SOCKET) {
3361  yclosesocket(SSDP->request_sock[i]);
3362  SSDP->request_sock[i] = INVALID_SOCKET;
3363  }
3364  if (SSDP->notify_sock[i] != INVALID_SOCKET) {
3365  yclosesocket(SSDP->notify_sock[i]);
3366  SSDP->notify_sock[i] = INVALID_SOCKET;
3367  }
3368  }
3369  SSDP->started--;
3370 }
#define YOCTO_SERIAL_SEED_SIZE
Definition: ydef.h:430
yUrlRef url
Definition: yproto.h:799
u8 stream
Definition: ydef.h:845
static int GenereateWebSockeyKey(const u8 *url, u32 urllen, char *buffer)
Definition: ytcp.c:1783
#define ysend(skt, buf, len, flags)
Definition: ytcp.c:214
u64 first_write_tm
Definition: yproto.h:846
YSOCKET request_sock[NB_OS_IFACES]
Definition: ytcp.h:169
u32 uploadRate
Definition: yproto.h:791
static void yHTTPCloseReqEx(struct _RequestSt *req, int canReuseSocket)
Definition: ytcp.c:959
void yThreadKill(yThread *yth)
Definition: ythread.c:343
int rw_access
Definition: yproto.h:801
#define YSTREAM_NOTICE
Definition: ydef.h:526
int handleNetNotification(HubSt *hub)
Definition: yapi.c:1677
#define YSSDP_PORT
Definition: ydef.h:858
int nbDetectedIfaces
Definition: ytcp.c:2814
#define WS_MAX_DATA_LEN
Definition: ytcp.c:1837
#define YOCTO_WEBSOCKET_MAGIC_LEN
Definition: ydef.h:434
static void * ySSDP_thread(void *ctx)
Definition: ytcp.c:3159
static int VerifyWebsocketKey(const char *data, u16 hdrlen, const char *reference, u16 reference_len)
Definition: ytcp.c:1800
Definition: yhash.h:175
YSOCKET skt
Definition: yproto.h:833
int yWaitForEvent(yEvent *ev, int time)
Definition: ythread.c:196
#define YERRMSG(code, message)
Definition: yproto.h:458
yCRITICAL_SECTION access
Definition: yproto.h:821
u8 * replybuf
Definition: yproto.h:868
yStrRef user
Definition: yproto.h:779
#define yrecv(skt, buf, len, flags)
Definition: ytcp.c:215
void * ws_thread(void *ctx)
Definition: ytcp.c:2537
YSOCKET skt
Definition: yproto.h:782
yStrRef pass
Definition: yproto.h:780
int bodysize
Definition: yproto.h:867
YSOCKET notify_sock[NB_OS_IFACES]
Definition: ytcp.h:170
void dumpYPerfEntry(yPerfMon *entry, const char *name)
Definition: ystream.c:2353
#define YSTREAM_TCP_ASYNCCLOSE
Definition: ydef.h:532
#define yNetLogErr()
Definition: ytcp.c:166
void yReqFree(struct _RequestSt *req)
Definition: ytcp.c:1606
u64 attemptDelay
Definition: yproto.h:816
ROSCPP_DECL void start()
#define YRand32()
Definition: ytcp.c:1672
Definition: ykey.h:74
char url[SSDP_URL_LEN]
Definition: ytcp.h:152
static int ws_parseIncommingFrame(HubSt *hub, u8 *buffer, int pktlen, char *errmsg)
Definition: ytcp.c:1954
u64 time
Definition: ytcp.c:323
struct _RequestSt * requests
Definition: yproto.h:767
void yInitializeCriticalSection(yCRITICAL_SECTION *cs)
Definition: ythread.c:629
u64 maxAge
Definition: ytcp.h:154
void yFifoCleanup(yFifoBuf *buf)
Definition: yfifo.c:70
int yReqIsAsync(struct _RequestSt *req)
Definition: ytcp.c:1596
int yReqSelect(struct _RequestSt *tcpreq, u64 ms, char *errmsg)
Definition: ytcp.c:1470
#define yNetSetErr()
Definition: ytcp.h:84
yHash yStrRef
Definition: ydef.h:214
static int ws_sendAuthenticationMeta(HubSt *hub, char *errmsg)
Definition: ytcp.c:1896
yFifoBuf mainfifo
Definition: yproto.h:783
static int ws_sendFrame(HubSt *hub, int stream, int tcpchan, const u8 *data, int datalen, char *errmsg)
Definition: ytcp.c:1843
int ySSDPDiscover(SSDPInfos *SSDP, char *errmsg)
Definition: ytcp.c:3233
int replysize
Definition: yproto.h:870
f
#define USB_META_WS_ANNOUNCE
Definition: ydef.h:779
yCRITICAL_SECTION access
Definition: yproto.h:860
#define TCPREQ_IN_USE
Definition: yproto.h:829
yHash yUrlRef
Definition: ydef.h:215
struct USB_Meta_Pkt::@30 announce
u32 tcpRoundTripTime
Definition: yproto.h:789
static int yTcpCheckSocketStillValid(YSOCKET skt, char *errmsg)
Definition: ytcp.c:569
int yReqGet(struct _RequestSt *req, u8 **buffer)
Definition: ytcp.c:1504
u64 timeout_tm
Definition: yproto.h:879
int yStartWakeUpSocket(WakeUpSocket *wuce, char *errmsg)
Definition: ytcp.c:225
u16 yPushFifo(yFifoBuf *buf, const u8 *data, u16 datalen)
Definition: yfifo.c:143
#define TCPLOG(fmt, args...)
Definition: yproto.h:325
int yReqRead(struct _RequestSt *req, u8 *buffer, int len)
Definition: ytcp.c:1525
ssdpHubDiscoveryCallback callback
Definition: ytcp.h:168
u32 ip
Definition: ytcp.c:322
static int ws_requestStillPending(HubSt *hub)
Definition: ytcp.c:2189
#define SEND_NOSIGPIPE
Definition: ytcp.h:74
u16 yPeekFifo(yFifoBuf *buf, u8 *data, u16 datalen, u16 startofs)
Definition: yfifo.c:263
#define WSLOG(fmt, args...)
Definition: yproto.h:388
#define WS_CONNEXION_TIMEOUT
Definition: ytcp.c:1836
int websocket_key_len
Definition: yproto.h:775
#define USB_META_ACK_UPLOAD
Definition: ydef.h:782
XmlRpcServer s
static const char * ws_header_end
Definition: ytcp.c:1670
u8 totalBytes[4]
Definition: ydef.h:832
#define YOCTO_BASE_SERIAL_LEN
Definition: ydef.h:421
#define YSTRCAT(dst, dstsize, src)
Definition: yproto.h:235
static const char * discovery
Definition: ytcp.c:2926
u64 bws_timeout_tm
Definition: yproto.h:785
yContextSt * yContext
Definition: ystream.c:59
int requestpos
Definition: yproto.h:845
#define yFifoInit(fifo, buffer, len)
Definition: yfifo.h:84
#define REQLOG(fmt, args...)
Definition: yproto.h:340
u64 bws_open_tm
Definition: yproto.h:784
void(* RequestProgress)(void *context, u32 acked, u32 totalbytes)
Definition: yproto.h:855
#define YSTATIC
Definition: ydef.h:292
static int ws_thread_select(struct _WSNetHubSt *base_req, u64 ms, WakeUpSocket *wuce, char *errmsg)
Definition: ytcp.c:2455
WSChanSt chan[MAX_ASYNC_TCPCHAN]
Definition: yproto.h:792
Definition: ytcp.h:148
u8 * ySHA1(const char *text)
Definition: ykey.c:425
#define REPORT_ERR(msg)
Definition: ytcp.h:82
int yThreadCreate(yThread *yth, void *(*fun)(void *), void *arg)
Definition: ythread.c:293
int yReqMultiSelect(struct _RequestSt **tcpreq, int size, u64 ms, WakeUpSocket *wuce, char *errmsg)
Definition: ytcp.c:1479
u8 * fifo_buffer
Definition: yproto.h:793
#define MAX_ASYNC_TCPCHAN
Definition: ydef.h:771
static int uuidToSerial(const char *uuid, char *serial)
Definition: ytcp.c:2953
#define YERR(code)
Definition: yproto.h:456
#define SSDP_URL_LEN
Definition: ytcp.h:146
#define dbglog(args...)
Definition: yproto.h:413
char * s_realm
Definition: yproto.h:742
#define YSTREAM_REPORT
Definition: ydef.h:527
static void ws_closeBaseSocket(struct _WSNetHubSt *base_req)
Definition: ytcp.c:2439
std_msgs::Header * header(M &m)
u32 remoteNounce
Definition: yproto.h:777
u16 yFifoGetUsed(yFifoBuf *buf)
Definition: yfifo.c:385
#define YPROPERR(call)
Definition: yproto.h:455
struct USB_Meta_Pkt::@32 error
#define USB_META_WS_AUTHENTICATION_SIZE
Definition: ydef.h:788
int ySetErr(int code, char *outmsg, const char *erreur, const char *file, u32 line)
Definition: ystream.c:72
DnsCache dnsCache[YDNS_CACHE_SIZE]
Definition: ytcp.c:326
yUrlRef url
Definition: ytcp.c:321
u64 lastUploadAckTime
Definition: yproto.h:763
yAsbUrlType yHashGetUrlPort(yUrlRef urlref, char *url, u16 *port, yAsbUrlProto *proto, yStrRef *user, yStrRef *password)
Definition: yhash.c:579
#define YOCTO_SERIAL_LEN
Definition: ydef.h:420
void yLeaveCriticalSection(yCRITICAL_SECTION *cs)
Definition: ythread.c:672
char errmsg[YOCTO_ERRMSG_LEN]
Definition: yproto.h:874
char serial[YOCTO_SERIAL_LEN]
Definition: ytcp.h:150
HTTPReqSt http
Definition: yproto.h:886
#define YOCTO_ERRMSG_LEN
Definition: ydef.h:418
static int yHTTPOpenReqEx(struct _RequestSt *req, u64 mstimout, char *errmsg)
Definition: ytcp.c:838
#define USB_META_WS_PROTO_V2
Definition: ydef.h:793
u32 ip
Definition: ytcp.h:134
Definition: yproto.h:798
#define INTEL_U32(NUM)
Definition: ydef.h:982
char * yHashGetStrPtr(yHash yhash)
Definition: yhash.c:401
#define YSTRNICMP(A, B, len)
Definition: yproto.h:229
u16 maxtcpws
Definition: ydef.h:813
static int yWSSelectReq(struct _RequestSt *req, u64 mstimeout, char *errmsg)
Definition: ytcp.c:1202
int retryCount
Definition: yproto.h:813
int yTcpDownload(const char *host, const char *url, u8 **out_buffer, u32 mstimeout, char *errmsg)
Definition: ytcp.c:702
#define INTEL_U16(NUM)
Definition: ydef.h:981
int yDringWakeUpSocket(WakeUpSocket *wuce, u8 signal, char *errmsg)
Definition: ytcp.c:263
static int ws_processRequests(HubSt *hub, char *errmsg)
Definition: ytcp.c:2213
static void ws_appendTCPData(RequestSt *req, u8 *buffer, int pktlen, int isClose)
Definition: ytcp.c:1923
u8 tcpchan
Definition: ydef.h:831
#define YSTREAM_REPORT_V2
Definition: ydef.h:529
#define DEFAULT_TCP_MAX_WINDOW_SIZE
Definition: ytcp.c:396
int replybufsize
Definition: yproto.h:869
char * bodybuf
Definition: yproto.h:865
Definition: yfifo.h:59
#define USB_META_WS_AUTH_FLAGS_RW
Definition: ydef.h:796
u32 notifAbsPos
Definition: yproto.h:814
#define YSTREAM_NOTICE_V2
Definition: ydef.h:530
int yReqOpen(struct _RequestSt *req, int wait_for_start, int tcpchan, const char *request, int reqlen, u64 mstimeout, yapiRequestAsyncCallback callback, void *context, RequestProgress progress_cb, void *progress_ctx, char *errmsg)
Definition: ytcp.c:1362
char uuid[SSDP_UUID_LEN]
Definition: ytcp.h:151
#define YSTREAM_TCP_CLOSE
Definition: ydef.h:525
enum WS_BASE_STATE base_state
Definition: yproto.h:771
YRETCODE
Definition: ydef.h:376
void ySSDPStop(SSDPInfos *SSDP)
Definition: ytcp.c:3330
int retryCount
Definition: yproto.h:872
os_ifaces detectedIfaces[NB_OS_IFACES]
Definition: ytcp.c:2813
#define SOCK_ERR
Definition: ytcp.h:80
YSOCKET listensock
Definition: ytcp.h:94
char websocket_key[32]
Definition: yproto.h:774
static int yTcpOpen(YSOCKET *newskt, u32 ip, u16 port, u64 mstimeout, char *errmsg)
Definition: ytcp.c:398
u8 metaType
Definition: ydef.h:800
HubSt * hub
Definition: yproto.h:859
void yFreeWakeUpSocket(WakeUpSocket *wuce)
Definition: ytcp.c:281
static int yHTTPMultiSelectReq(struct _RequestSt **reqs, int size, u64 ms, WakeUpSocket *wuce, char *errmsg)
Definition: ytcp.c:990
void MD5Calculate(HASH_SUM *ctx, u8 digest[16])
Definition: ykey.c:671
NET_HUB_STATE state
Definition: yproto.h:810
yAsbUrlProto proto
Definition: yproto.h:809
#define SSDP_NOTIFY
Definition: ytcp.c:2935
void yDupSet(char **storage, const char *val)
Definition: ytcp.c:128
void yThreadRequestEnd(yThread *yth)
Definition: ythread.c:331
#define yApproximateSleep(ms)
Definition: yproto.h:433
void * ctx
Definition: ythread.h:96
u32 netmask
Definition: ytcp.h:135
char serial[YOCTO_SERIAL_LEN]
Definition: ydef.h:815
#define NB_OS_IFACES
Definition: ytcp.h:163
#define YSTRCPY(dst, dstsize, src)
Definition: yproto.h:234
u64 read_tm
Definition: yproto.h:878
void * progressCtx
Definition: yproto.h:885
#define __FILE_ID__
Definition: ytcp.c:40
struct USB_Meta_Pkt::@33 uploadAck
int ySSDPStart(SSDPInfos *SSDP, ssdpHubDiscoveryCallback callback, char *errmsg)
Definition: ytcp.c:3253
static void ySSDP_parseSSPDMessage(SSDPInfos *SSDP, char *message, int msg_len)
Definition: ytcp.c:3060
static int yTcpRead(YSOCKET skt, u8 *buffer, int len, char *errmsg)
Definition: ytcp.c:679
#define YSSDP_MCAST_ADDR_STR
Definition: ydef.h:859
char * name
Definition: yproto.h:808
char * s_opaque
Definition: yproto.h:745
u64 lastAttempt
Definition: yproto.h:815
WSNetHub ws
Definition: yproto.h:824
#define USB_META_WS_PROTO_V1
Definition: ydef.h:792
#define YERRTO(code, buffer)
Definition: yproto.h:457
u8 sha1[20]
Definition: ydef.h:822
yAsbUrlProto proto
Definition: yproto.h:881
static void yTcpClose(YSOCKET skt)
Definition: ytcp.c:560
u8 s_ha1[16]
Definition: yproto.h:746
RequestState state
Definition: yproto.h:862
WSReqSt ws
Definition: yproto.h:887
void yInitWakeUpSocket(WakeUpSocket *wuce)
Definition: ytcp.c:218
int remoteVersion
Definition: yproto.h:776
RequestProgress progressCb
Definition: yproto.h:884
static void ySSDPCheckExpiration(SSDPInfos *SSDP)
Definition: ytcp.c:3039
YSTATIC int yDetectNetworkInterfaces(u32 only_ip)
Definition: ytcp.c:2858
int YSOCKET
Definition: ydef.h:236
#define YASSERT(x)
Definition: yproto.h:454
int yReqHasPending(struct _HubSt *hub)
Definition: ytcp.c:1629
char * s_pwd
Definition: yproto.h:743
#define YSPRINTF
Definition: yproto.h:238
yEvent finished
Definition: yproto.h:861
void * context
Definition: yproto.h:883
#define USB_META_WS_ANNOUNCE_SIZE
Definition: ydef.h:787
#define YSSDP_URN_YOCTOPUCE
Definition: ydef.h:861
#define YDNS_CACHE_VALIDITY
Definition: ytcp.c:319
static int ws_openBaseSocket(struct _WSNetHubSt *hub, yUrlRef url, const char *request, int request_len, int mstimout, char *errmsg)
Definition: ytcp.c:2353
static u32 resolveDNSCache(yUrlRef url, char *errmsg)
Definition: ytcp.c:329
SSDP_CACHE_ENTRY * SSDPCache[NB_SSDP_CACHE_ENTRY]
Definition: ytcp.h:172
u16 yFifoGetFree(yFifoBuf *buf)
Definition: yfifo.c:401
int dbglogf(const char *fileid, int line, const char *fmt,...)
Definition: ystream.c:222
void yDigestAuthorization(char *buf, int bufsize, const char *user, const char *realm, const u8 *ha1, const char *nonce, const char *opaque, u32 *nc, const char *method, const char *uri)
Definition: ykey.c:269
#define YSTREAM_META
Definition: ydef.h:528
char * s_user
Definition: yproto.h:741
void yResetEvent(yEvent *ev)
Definition: ythread.c:187
void yEnterCriticalSection(yCRITICAL_SECTION *cs)
Definition: ythread.c:647
#define yFree(ptr)
Definition: yproto.h:199
int replypos
Definition: yproto.h:871
YSOCKET signalsock
Definition: ytcp.h:95
u32 nounce
Definition: yproto.h:778
u32 lastUploadAckBytes
Definition: yproto.h:762
int bodybufsize
Definition: yproto.h:866
#define SSDP_HTTP
Definition: ytcp.c:2937
#define YPERF_TCP_ENTER(NAME)
Definition: ytcp.c:122
void(* ssdpHubDiscoveryCallback)(const char *serial, const char *urlToRegister, const char *urlToUnregister)
Definition: ytcp.h:160
yAsbUrlProto
Definition: yhash.h:179
struct USB_Meta_Pkt::@31 auth
int s_next_async_id
Definition: yproto.h:781
static int yNetLogErrEx(u32 line, unsigned err)
Definition: ytcp.c:167
void(* yapiRequestAsyncCallback)(void *context, const u8 *result, u32 resultlen, int retcode, const char *errmsg)
Definition: ydef.h:864
int yConsumeWakeUpSocket(WakeUpSocket *wuce, char *errmsg)
Definition: ytcp.c:271
#define INVALID_SOCKET
Definition: ytcp.h:63
#define yMalloc(size)
Definition: yproto.h:198
int CheckWSAuth(u32 nonce, const u8 *ha1, const u8 *to_verify, u8 *out)
Definition: ykey.c:158
char errmsg[YOCTO_ERRMSG_LEN]
Definition: yproto.h:820
static u16 Base64Encode(const u8 *cSourceData, u16 wSourceLen, u8 *cDestData, u16 wDestLen)
Definition: ytcp.c:1702
#define YSTRLEN(str)
Definition: yproto.h:230
char serial[YOCTO_SERIAL_LEN]
Definition: yproto.h:773
#define YPERF_TCP_LEAVE(NAME)
Definition: ytcp.c:123
#define SOCKET_ERROR
Definition: ytcp.h:62
void ComputeAuthHA1(u8 *ha1, const char *user, const char *pass, const char *realm)
Definition: ykey.c:77
#define closesocket(s)
Definition: ytcp.h:64
#define TOSTRING(x)
Definition: yproto.h:423
void yDeleteCriticalSection(yCRITICAL_SECTION *cs)
Definition: ythread.c:682
static int yWSOpenReqEx(struct _RequestSt *req, int tcpchan, u64 mstimeout, char *errmsg)
Definition: ytcp.c:1146
void MD5Initialize(HASH_SUM *ctx)
Definition: ykey.c:540
void yTcpShutdown(void)
Definition: ytcp.c:382
int yTryEnterCriticalSection(yCRITICAL_SECTION *cs)
Definition: ythread.c:657
int headerbufsize
Definition: yproto.h:864
#define YSTREAM_TCP_NOTIF
Definition: ydef.h:531
static char hexatochar(char hi_c, char lo_c)
Definition: ytcp.c:2943
struct _RequestSt * next
Definition: yproto.h:842
Definition: ytcp.c:320
static void ySSDPUpdateCache(SSDPInfos *SSDP, const char *uuid, const char *url, int cacheValidity)
Definition: ytcp.c:2993
void ySetEvent(yEvent *ev)
Definition: ythread.c:175
int yNetSetErrEx(u32 line, unsigned err, char *errmsg)
Definition: ytcp.c:141
WakeUpSocket wuce
Definition: yproto.h:806
void yReqClose(struct _RequestSt *req)
Definition: ytcp.c:1561
#define INVALID_HASH_IDX
Definition: ydef.h:219
static int yTcpCheckReqTimeout(struct _RequestSt *req, char *errmsg)
Definition: ytcp.c:792
void yCreateManualEvent(yEvent *ev, int initialState)
Definition: ythread.c:167
RequestSt * tcpreq[ALLOC_YDX_PER_HUB]
Definition: yproto.h:942
struct _RequestSt * yReqAlloc(struct _HubSt *hub)
Definition: ytcp.c:1337
char * s_nonce
Definition: yproto.h:744
u64 YAPI_FUNCTION_EXPORT yapiGetTickCount(void)
Definition: yapi.c:2713
int errcode
Definition: yproto.h:873
YSOCKET reuseskt
Definition: yproto.h:834
#define ysocket(domain, type, protocol)
Definition: ytcp.c:213
static const char * ws_header_start
Definition: ytcp.c:1669
#define YOCTO_HOSTNAME_NAME
Definition: yhash.h:171
u64 write_tm
Definition: yproto.h:877
u32 yResolveDNS(const char *name, char *errmsg)
Definition: ytcp.c:296
u32 flags
Definition: yproto.h:880
void yThreadSignalEnd(yThread *yth)
Definition: ythread.c:326
u32 tcpMaxWindowSize
Definition: yproto.h:790
u8 encaps
Definition: ydef.h:848
u16 flags
Definition: ydef.h:820
yThread thread
Definition: ytcp.h:171
#define USB_META_WS_AUTH_FLAGS_VALID
Definition: ydef.h:795
int yParseWWWAuthenticate(char *replybuf, int replysize, char **method, char **realm, char **qop, char **nonce, char **opaque)
Definition: ykey.c:192
int yTcpInit(char *errmsg)
Definition: ytcp.c:364
int errcode
Definition: yproto.h:819
#define TCPREQ_KEEPALIVE
Definition: yproto.h:828
u8 version
Definition: ydef.h:812
char * headerbuf
Definition: yproto.h:863
u32 nonce
Definition: ydef.h:814
u64 next_transmit_tm
Definition: yproto.h:787
yapiRequestAsyncCallback callback
Definition: yproto.h:882
void yCloseEvent(yEvent *ev)
Definition: ythread.c:223
void YAPI_FUNCTION_EXPORT yapiUnregisterHub(const char *url)
Definition: yapi.c:4430
u64 lastUploadRateTime
Definition: yproto.h:765
static int yTcpWrite(YSOCKET skt, const char *buffer, int len, char *errmsg)
Definition: ytcp.c:626
int started
Definition: ytcp.h:167
#define YOCTO_WEBSOCKET_MAGIC
Definition: ydef.h:433
#define YIO_IDLE_TCP_TIMEOUT
Definition: ydef.h:253
int yThreadMustEnd(yThread *yth)
Definition: ythread.c:338
int channel
Definition: yproto.h:839
u64 last_write_tm
Definition: yproto.h:847
#define USB_META_WS_AUTHENTICATION
Definition: ydef.h:780
u8 tcpchan
Definition: ydef.h:844
#define yclosesocket(skt)
Definition: ytcp.c:212
static void ws_threadUpdateRetryCount(HubSt *hub)
Definition: ytcp.c:2521
u32 lastUploadRateBytes
Definition: yproto.h:764
u64 detectedTime
Definition: ytcp.h:153
#define YISERR(retcode)
Definition: ydef.h:394
int requestsize
Definition: yproto.h:844
u64 open_tm
Definition: yproto.h:875
u16 htmlcode
Definition: ydef.h:827
yFifoBuf not_fifo
Definition: yproto.h:811
#define YSTRNCMP(A, B, len)
Definition: yproto.h:227
int yThreadIsRunning(yThread *yth)
Definition: ythread.c:311
#define ALLOC_YDX_PER_HUB
Definition: yproto.h:728
void yThreadSignalStart(yThread *yth)
Definition: ythread.c:318
int asyncId
Definition: yproto.h:840
#define YSTRCMP(A, B)
Definition: yproto.h:226
#define DEFAULT_TCP_ROUND_TRIP_TIME
Definition: ytcp.c:395
#define YSTREAM_EMPTY
Definition: ydef.h:523
yCRITICAL_SECTION access
Definition: yproto.h:766
u16 ySeekFifo(yFifoBuf *buf, const u8 *pattern, u16 patlen, u16 startofs, u16 searchlen, u8 bTextCompare)
Definition: yfifo.c:367
#define YSTREAM_TCP
Definition: ydef.h:524
#define NB_SSDP_CACHE_ENTRY
Definition: ytcp.h:162
u32 flags
Definition: ytcp.h:133
u16 yPopFifo(yFifoBuf *buf, u8 *data, u16 datalen)
Definition: yfifo.c:187
int yReqIsEof(struct _RequestSt *req, char *errmsg)
Definition: ytcp.c:1486
int YFOPEN(FILE **f, const char *filename, const char *mode)
Definition: ystream.c:130
#define OS_IFACE_CAN_MCAST
Definition: ytcp.h:130
u64 connectionTime
Definition: yproto.h:788
void MD5AddData(HASH_SUM *ctx, const u8 *buf, u32 len)
Definition: ykey.c:634
static void yWSCloseReqEx(struct _RequestSt *req, int takeCS)
Definition: ytcp.c:1215
#define YDNS_CACHE_SIZE
Definition: ytcp.c:318
HTTPNetHub http
Definition: yproto.h:823
#define USB_META_WS_ERROR
Definition: ydef.h:781
u8 * requestbuf
Definition: yproto.h:843


yoctopuce_altimeter
Author(s): Anja Sheppard
autogenerated on Mon Jun 10 2019 15:49:13