license change, XYSSL upgrade
[syren.git] / src / syren_tcp.c
blobc1e3be6e6d531f06231755b153d45d1f54621b69
1 /*
2 Syren -- a lightweight downloader for Linux/BSD/Win/MacOSX
3 inspired by Axel Copyright 2001-2002 Wilmer van der Gaast
4 version 0.0.6 (atomic alien)
5 coded by Ketmar // Avalon Group
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License with
18 the Debian GNU/Linux distribution in file /usr/doc/copyright/GPL;
19 if not, write to the Free Software Foundation, Inc., 59 Temple Place,
20 Suite 330, Boston, MA 02111-1307 USA
23 Syren tcp utilities
25 #ifndef _SYREN_TCP_C
26 #define _SYREN_TCP_C
28 #include "syren_tcp.h"
31 #ifdef WINDOZE
32 #define SY_SOCK_MSG_NO_SIGNAL 0
33 #else
34 #ifdef __APPLE__
35 #define SY_SOCK_MSG_NO_SIGNAL SO_NOSIGPIPE
36 #else
37 #define SY_SOCK_MSG_NO_SIGNAL MSG_NOSIGNAL
38 #endif
39 #endif
42 #ifndef WINDOZE
43 #include <errno.h>
44 #include <string.h>
45 #endif
47 #ifdef SYOPT_ALLOW_HTTPS
48 #include "xyssl/ssl.h"
49 #include "xyssl/havege.h"
52 typedef struct {
53 havege_state hs;
54 ssl_context ssl;
55 ssl_session ssn;
56 } TSySSLInfo;
59 static int SyTCP_SSLSend (void *sock, unsigned char *buf, int len) {
60 return send(((TSySocket *)sock)->fd, (void *)buf, len, SY_SOCK_MSG_NO_SIGNAL);
63 static int SyTCP_SSLRecv (void *sock, unsigned char *buf, int len) {
64 return recv(((TSySocket *)sock)->fd, (void *)buf, len, 0);
66 #endif
69 static int SyTCPLastError (void) {
70 #ifdef WINDOZE
71 return WSAGetLastError();
72 #else
73 return errno;
74 #endif /* WINDOZE */
78 /* returns allocated string or NULL */
79 char *SyTCPGetLastErrorMsg (TSySocket *fd) {
80 #ifdef WINDOZE
81 if (!fd || !fd->errCode) return NULL;
82 return SySPrintf("socket error: %i", fd->errCode);
83 #else
84 char mbuf[1024], *estr;
86 #ifdef SY_XYZ_TMP
87 #undef SY_XYZ_TMP
88 #endif
90 #ifdef _GNU_SOURCE
91 #define SY_XYZ_TMP
92 #else
93 #if (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600)
94 #else
95 #define SY_XYZ_TMP
96 #endif
97 #endif /* _GNU_SOURCE */
99 if (!fd || !fd->errCode) return NULL;
100 memset(mbuf, 0, sizeof(mbuf));
101 estr = strerror_r(fd->errCode, mbuf, sizeof(mbuf));
102 #ifdef SY_XYZ_TMP
103 /* GNU */
104 return SyStrNew(estr, -1);
105 #else
106 /* XSI-compliant */
107 if (!estr) return SySPrintf("(%i) %s", fd->errCode, mbuf);
108 return NULL;
109 #endif /* XSI test */
111 #ifdef SY_XYZ_TMP
112 #undef SY_XYZ_TMP
113 #endif
114 #endif /* WINDOZE */
118 TSyResult SyGetIFIP (char *ip, const char *iface) {
119 #ifdef WINDOZE
120 *ip = '\0'; return SY_ERROR;
121 #else
122 int res;
123 struct ifreq ifr;
124 struct in_addr inp;
125 if (!ip) return SY_ERROR;
126 *ip = '\0';
127 if (!iface || !(*iface)) return SY_ERROR;
129 if (inet_aton(iface, &inp)) {
130 strcpy(ip, inet_ntoa(inp));
131 return SY_OK;
134 if (strlen(iface) > IFNAMSIZ) return SY_ERROR;
135 int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
136 memset(&ifr, 0, sizeof(struct ifreq));
137 strcpy(ifr.ifr_name, iface);
138 ifr.ifr_addr.sa_family = AF_INET;
139 res = ioctl(fd, SIOCGIFADDR, &ifr);
140 SySocketClose(fd);
141 if (!res) {
142 struct sockaddr_in *x = (struct sockaddr_in *)&ifr.ifr_addr;
143 strcpy(ip, inet_ntoa(x->sin_addr));
144 return SY_OK;
146 return SY_ERROR;
147 #endif
151 TSyResult SyTCPInitSocket (TSySocket *fd) {
152 #ifdef SYOPT_ALLOW_HTTPS
153 memset(fd, 0, sizeof(TSySocket));
154 /*fd->usessl = 0;*/
155 #endif
156 fd->errCode = 0;
157 fd->fd = -1;
158 return SY_OK;
162 #ifdef SYOPT_ALLOW_HTTPS
163 TSyResult SyTCPInitSSL (TSySocket *fd, int timeout) {
164 TSySSLInfo *si;
166 fd->errCode = 0;
167 if (fd->fd < 0) return SY_ERROR;
168 if (fd->usessl) return SY_ERROR;
169 if (!fd->sslinfo) {
170 fd->sslinfo = calloc(1, sizeof(TSySSLInfo));
171 if (!fd->sslinfo) return SY_ERROR;
173 si = fd->sslinfo;
174 havege_init(&(si->hs));
175 memset(&(si->ssn), 0, sizeof(ssl_session));
177 if (ssl_init(&(si->ssl))) return SY_ERROR;
178 /*ssl_set_debuglvl(&(si->ssl), 0);*/
179 ssl_set_endpoint(&(si->ssl), SSL_IS_CLIENT);
180 ssl_set_authmode(&(si->ssl), SSL_VERIFY_NONE); /*!!!*/
181 ssl_set_rng(&(si->ssl), havege_rand, &(si->hs));
182 ssl_set_bio(&(si->ssl), SyTCP_SSLRecv, (void *)fd, SyTCP_SSLSend, (void *)fd);
183 ssl_set_ciphers(&(si->ssl), ssl_default_ciphers);
185 ssl_set_session(&(si->ssl), 1, timeout, &(si->ssn));
187 fd->usessl = 1;
188 return SY_OK;
190 #endif
193 TSyResult SyTCPCloseSocket (TSySocket *fd) {
194 #ifdef SYOPT_ALLOW_HTTPS
195 TSySSLInfo *si;
196 #endif
198 if (fd->fd >= 0) {
199 #ifdef SYOPT_ALLOW_HTTPS
200 if (fd->usessl && fd->sslinfo) {
201 si = fd->sslinfo;
202 if (fd->usessl > 1) ssl_close_notify(&(si->ssl));
203 ssl_free(&(si->ssl));
204 memset(&(si->ssl), 0, sizeof(si->ssl));
205 fd->usessl = 0;
207 if (fd->sslinfo) free(fd->sslinfo);
208 #endif
209 SySocketClose(fd->fd);
210 fd->fd = -1;
212 return SY_OK;
216 TSyResult SyTCPConnect (TSySocket *fd, char *hostname, int port, const char *iface, int timeout, const TSyPrintStr *pfn) {
217 struct hostent *host = NULL;
218 struct sockaddr_in addr;
219 struct sockaddr_in local;
220 char ifip[64];
221 #ifdef WINDOZE
222 DWORD tmp;
223 #else
224 int val, size;
225 struct timeval tv;
226 #endif
228 SyMessage(pfn, SY_MSG_NOTICE, "connecting to %s:%i", hostname, port);
230 SyTCPInitSocket(fd);
231 if (!hostname || !(*hostname)) return SY_ERROR;
232 if (iface && *iface && strcmp(iface, "-")) {
233 SyMessage(pfn, SY_MSG_NOTICE, " resolving local intefrace %s...\n", iface);
234 if (SyGetIFIP(ifip, iface) != SY_OK) {
235 SyMessage(pfn, SY_MSG_ERROR, "can't determine interface IP for %s", iface);
236 return SY_ERROR;
238 } else *ifip = '\0';
240 SyMessage(pfn, SY_MSG_NOTICE, " resolving %s", hostname);
241 host = gethostbyname(hostname);
242 if (!host || !host->h_name || !*host->h_name) {
243 fd->errCode = SyTCPLastError();
244 SyMessage(pfn, SY_MSG_ERROR, "can't resolve %s", hostname);
245 return SY_ERROR;
248 fd->fd = socket(AF_INET, SOCK_STREAM, 0);
249 if (fd->fd == -1) {
250 fd->errCode = SyTCPLastError();
251 SyMessage(pfn, SY_MSG_ERROR, "can't create socket");
252 return SY_ERROR;
254 if (*ifip) {
255 SyMessage(pfn, SY_MSG_NOTICE, " binding to local intefrace [%s]...\n", ifip);
256 local.sin_family = AF_INET;
257 local.sin_port = 0;
258 local.sin_addr.s_addr = inet_addr(ifip);
259 if (bind(fd->fd, (struct sockaddr *)&local, sizeof(struct sockaddr_in)) == -1) {
260 fd->errCode = SyTCPLastError();
261 SyTCPCloseSocket(fd);
262 SyMessage(pfn, SY_MSG_ERROR, "can't bind to local interface %s", ifip);
263 return SY_ERROR;
267 addr.sin_family = AF_INET;
268 addr.sin_port = htons(port);
269 addr.sin_addr = *((struct in_addr *)host->h_addr);
270 if (*ifip) SyMessage(pfn, SY_MSG_NOTICE, " connecting to %s:%i (%s)", inet_ntoa(addr.sin_addr), port, ifip);
271 else SyMessage(pfn, SY_MSG_NOTICE, " connecting to %s:%i", inet_ntoa(addr.sin_addr), port);
273 if (timeout > 0) {
274 #ifdef WINDOZE
275 tmp = timeout*1000;
276 if (setsockopt(fd->fd, SOL_SOCKET, SO_RCVTIMEO, (void *)(&tmp), 4)) {
277 SyMessage(pfn, SY_MSG_WARNING, " can't set receive timeout");
279 tmp = timeout*1000;
280 if (setsockopt(fd->fd, SOL_SOCKET, SO_SNDTIMEO, (void *)(&tmp), 4)) {
281 SyMessage(pfn, SY_MSG_WARNING, " can't set send timeout");
283 tmp = 1; setsockopt(fd->fd, SOL_SOCKET, SO_KEEPALIVE, (void *)(&tmp), 4);
284 #else
285 tv.tv_sec = timeout; tv.tv_usec = 0; size = sizeof(tv);
286 if (setsockopt(fd->fd, SOL_SOCKET, SO_RCVTIMEO, &tv, size)) {
287 SyMessage(pfn, SY_MSG_WARNING, " can't set receive timeout");
289 tv.tv_sec = timeout; tv.tv_usec = 0; size = sizeof(tv);
290 if (setsockopt(fd->fd, SOL_SOCKET, SO_SNDTIMEO, &tv, size)) {
291 SyMessage(pfn, SY_MSG_WARNING, " can't set send timeout");
293 val = 1; size = sizeof(val);
294 setsockopt(fd->fd, SOL_SOCKET, SO_KEEPALIVE, &val, size);
295 #endif
298 if (connect(fd->fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0) {
299 fd->errCode = SyTCPLastError();
300 SyTCPCloseSocket(fd);
301 SyMessage(pfn, SY_MSG_ERROR, "can't connect to %s:%i", inet_ntoa(addr.sin_addr), port);
302 return SY_ERROR;
304 SyMessage(pfn, SY_MSG_NOTICE, " connected to %s:%i", inet_ntoa(addr.sin_addr), port);
306 return SY_OK;
310 TSyResult SyTCPSend (TSySocket *fd, const void *buf, int bufSize) {
311 const char *c = (char *)buf;
313 fd->errCode = 0;
314 if (!buf) return SY_OK;
315 while (bufSize > 0) {
316 #ifdef SYOPT_ALLOW_HTTPS
317 int wr, tosend;
318 if (fd->usessl) {
319 tosend = bufSize>SSL_MAX_CONTENT_LEN?SSL_MAX_CONTENT_LEN:bufSize;
320 wr = ssl_write(&(((TSySSLInfo *)fd->sslinfo)->ssl), (void *)buf, tosend);
321 if (wr == XYSSL_ERR_NET_TRY_AGAIN) continue;
322 if (wr >= 0) fd->usessl = 2;
323 } else wr = send(fd->fd, c, bufSize, SY_SOCK_MSG_NO_SIGNAL);
324 #else
325 int wr = send(fd->fd, c, bufSize, SY_SOCK_MSG_NO_SIGNAL);
326 #endif
327 if (wr <= 0) {
328 fd->errCode = SyTCPLastError();
329 return SY_ERROR;
331 c += wr; bufSize -= wr;
333 return SY_OK;
337 TSyResult SyTCPSendStr (TSySocket *fd, const char *str) {
338 if (fd < 0) return SY_ERROR;
339 if (!str || !(*str)) return SY_OK;
340 return SyTCPSend(fd, str, strlen(str));
344 /* <0: received (-res) bytes; read error */
345 int SyTCPReceiveEx (TSySocket *fd, void *buf, int bufSize, int allowPartial) {
346 char *c = (char *)buf;
347 int total = 0;
349 fd->errCode = 0;
350 if (fd->fd < 0) return 0;
351 while (bufSize > 0) {
352 #ifdef SYOPT_ALLOW_HTTPS
353 int rd, toread;
354 if (fd->usessl) {
355 toread = bufSize>SSL_MAX_CONTENT_LEN?SSL_MAX_CONTENT_LEN:bufSize;
356 rd = ssl_read(&(((TSySSLInfo *)fd->sslinfo)->ssl), buf, toread);
357 if (rd == XYSSL_ERR_NET_TRY_AGAIN) continue;
358 if (rd >= 0) fd->usessl = 2;
359 } else rd = recv(fd->fd, c, bufSize, 0);
360 #else
361 int rd = recv(fd->fd, c, bufSize, 0);
362 #endif
363 if (rd <= 0) fd->errCode = SyTCPLastError();
364 if (!rd) return total;
365 if (rd < 0) return -total;
366 c += rd; total += rd; bufSize -= rd;
367 if (allowPartial) break;
369 return total;
373 int SyTCPReceive (TSySocket *fd, void *buf, int bufSize) {
374 return SyTCPReceiveEx(fd, buf, bufSize, SY_TCP_DONT_ALLOWPARTIAL);
378 TSyResult SyTCPSendLine (const TSyPrintStr *pfn, int printLine, TSySocket *fd, const char *fmt, ...) {
379 TSyResult res;
380 int n, size = 100;
381 char *p, *np;
382 va_list ap;
384 if ((p = calloc(1, size+4)) == NULL) { SyMessage(pfn, SY_MSG_ERROR, "memory error"); return SY_ERROR; }
385 while (1) {
386 va_start(ap, fmt);
387 n = vsnprintf(p, size, fmt, ap);
388 va_end(ap);
389 if (n > -1 && n < size) break;
390 if (n > -1) size = n+1; else size *= 2;
391 if ((np = realloc(p, size+4)) == NULL) {
392 free(p);
393 SyMessage(pfn, SY_MSG_ERROR, "memory error");
394 return SY_ERROR;
396 p = np;
398 if (printLine) SyMessage(pfn, SY_MSG_NOTICE, " %s", p);
399 strcat(p, "\r\n");
400 res = SyTCPSendStr(fd, p);
401 free(p);
402 if (res != SY_OK) { SyMessage(pfn, SY_MSG_ERROR, "socket write error"); return SY_ERROR; }
403 return SY_OK;
407 char *SyTCPReceiveStrEx (TSySocket *fd, int maxSize, char *dest) {
408 char ch, *d, *chp;
409 int f, rcv;
411 fd->errCode = 0;
412 if (maxSize <= 0 || fd->fd < 0 || !dest) return NULL;
413 memset(dest, 0, maxSize+1);
414 d = dest;
415 while (maxSize) {
416 int rd;
417 #ifdef SYOPT_ALLOW_HTTPS
418 rd = 0;
419 #else
420 #ifndef WINDOZE
421 rd = recv(fd->fd, d, maxSize+2, MSG_PEEK);
422 #else
423 rd = 0;
424 #endif /* WINDOZE */
425 #endif /* SYOPT_ALLOW_HTTPS */
426 if (!rd) {
427 rcv = 1;
428 #ifdef SYOPT_ALLOW_HTTPS
429 if (fd->usessl) {
430 rd = ssl_read(&(((TSySSLInfo *)fd->sslinfo)->ssl), (void *)d, 1);
431 if (rd == XYSSL_ERR_NET_TRY_AGAIN) continue;
432 if (rd >= 0) fd->usessl = 2;
433 } else
434 #endif
435 rd = recv(fd->fd, d, 1, 0);
436 } else rcv = 0;
437 if (rd <= 0) fd->errCode = SyTCPLastError();
438 if (rd < 0) return NULL;
439 if (rd == 0) break; /* connection closed */
440 /* check for end of headers */
441 chp = d; f = 0;
442 while (f < rd) {
443 ch = *chp; f++;
444 if (!ch) return NULL; /* can't got zero byte here */
445 if (ch == '\n') {
446 /* yes! all chars are here */
447 if (!rcv) {
448 /* receive peeked bytes */
449 rd = recv(fd->fd, d, f, 0);
450 if (rd < 0) return NULL;
452 if (chp > dest && *(chp-1) == '\r') chp--; /* skip CR */
453 *chp = '\0';
454 return dest;
456 chp++;
458 if (!rcv) {
459 /* receive peeked bytes */
460 rd = recv(fd->fd, d, rd, 0);
461 if (rd <= 0) return NULL;
463 maxSize -= rd; d += rd;
465 /* headers too big */
466 return NULL;
470 char *SyTCPReceiveStr (TSySocket *fd, int maxSize) {
471 char *dest, *res;
473 fd->errCode = 0;
474 if (maxSize <= 0 || fd->fd < 0) return NULL;
475 dest = calloc(1, maxSize+8);
476 if (!dest) return NULL;
477 res = SyTCPReceiveStrEx(fd, maxSize, dest);
478 if (!res) { free(dest); return NULL; }
479 return dest;
483 /* return NULL if string was too big or on error;
484 tries to receive all headers
486 char *SyTCPReceiveHdrs (TSySocket *fd, int maxSize) {
487 char ch, *d, *chp, *dest;
488 int f, l, rcv;
490 fd->errCode = 0;
491 if (maxSize <= 0 || fd->fd < 0) return NULL;
492 dest = calloc(1, maxSize+8);
493 d = dest;
494 while (maxSize) {
496 #ifndef WINDOZE
497 int rd = recv(fd->fd, d, maxSize+2, MSG_PEEK);
498 #else
499 int rd = 0;
500 #endif
501 if (!rd) { rcv = 1; rd = recv(fd->fd, d, 1, 0); } else rcv = 0;
503 int rd;
504 #ifdef SYOPT_ALLOW_HTTPS
505 rd = 0;
506 #else
507 #ifndef WINDOZE
508 rd = recv(fd->fd, d, maxSize+2, MSG_PEEK);
509 #else
510 rd = 0;
511 #endif /* WINDOZE */
512 #endif /* SYOPT_ALLOW_HTTPS */
513 if (!rd) {
514 rcv = 1;
515 #ifdef SYOPT_ALLOW_HTTPS
516 if (fd->usessl) {
517 rd = ssl_read(&(((TSySSLInfo *)fd->sslinfo)->ssl), (void *)d, 1);
518 if (rd == XYSSL_ERR_NET_TRY_AGAIN) continue;
519 if (rd >= 0) fd->usessl = 2;
520 } else
521 #endif
522 rd = recv(fd->fd, d, 1, 0);
523 } else rcv = 0;
524 if (rd <= 0) fd->errCode = SyTCPLastError();
525 if (rd < 0) {
526 free(dest);
527 return NULL;
529 if (rd == 0) break; /* connection closed */
530 /* check for end of headers */
531 chp = d; f = 0;
532 while (f < rd) {
533 ch = *chp; chp++; f++;
534 if (!ch) { free(dest); return NULL; } /* can't got zero byte here */
535 if (ch == '\n') {
536 /* check for empty line */
537 l = (chp-dest)-2; /* prev ch index */
538 if (l >= 0 && dest[l] == '\r') l--; /* skip CR */
539 if (l >= 0 && dest[l] == '\n') {
540 /* yes! all headers are here */
541 if (!rcv) {
542 /* receive peeked bytes */
543 rd = recv(fd->fd, d, f, 0);
544 if (rd < 0) { free(dest); return NULL; }
546 *chp = '\0';
547 return dest;
551 if (!rcv) {
552 /* receive peeked bytes */
553 rd = recv(fd->fd, d, rd, 0);
554 if (rd <= 0) { free(dest); return NULL; }
556 maxSize -= rd; d += rd;
558 /* headers too big */
559 free(dest);
560 return NULL;
564 #endif