it is now possible to build Syren with GnuTLS (and GnuTLS will be used by default...
[syren.git] / src / sylib / syren_tcp.c
blobb837de55470e2fb7426a5d5b564bce13191b2c84
1 /*
2 Syren -- a lightweight downloader for Linux/BSD/MacOSX
3 inspired by Axel Copyright 2001-2002 Wilmer van der Gaast
4 version 0.0.6 (atomic alien)
5 coded by Ketmar // Vampire Avalon
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 __APPLE__
32 #define SY_SOCK_MSG_NO_SIGNAL SO_NOSIGPIPE
33 #else
34 #define SY_SOCK_MSG_NO_SIGNAL MSG_NOSIGNAL
35 #endif
38 #include <errno.h>
39 #include <string.h>
42 #ifdef SYOPT_ALLOW_HTTPS
44 #ifdef SY_USE_GNUTLS
45 # include <gnutls/gnutls.h>
46 # include <gnutls/x509.h>
48 #define SSL_MAX_CONTENT_LEN (32768)
49 static int gnutlsInited = 0;
52 typedef struct {
53 gnutls_certificate_credentials_t xcred;
54 gnutls_session_t session;
55 //int handshakeComplete;
56 } TSySSLInfo;
58 static int SyTCP_SSLSend (TSySSLInfo *si, const void *buf, size_t len) {
59 if (len < 1) return 0;
60 for (;;) {
61 int res = (int)gnutls_record_send(si->session, buf, len);
62 //if (res < 0) fprintf(stderr, "GnuTLS: error sending %d bytes (%d) :%s\n", (int)len, res, gnutls_strerror(res));
63 //if (res != GNUTLS_E_INTERRUPTED && res != GNUTLS_E_AGAIN) return res;
64 if (res >= 0 || gnutls_error_is_fatal(res)) return res;
68 static int SyTCP_SSLRecv (TSySSLInfo *si, void *buf, size_t len) {
69 if (len < 1) return 0;
70 for (;;) {
71 int res = (int)gnutls_record_recv(si->session, buf, len);
72 //if (res < 0) fprintf(stderr, "GnuTLS: error receiving %d bytes (%d) (fatal=%d) :%s\n", (int)len, res, gnutls_error_is_fatal(res), gnutls_strerror(res));
73 //if (res != GNUTLS_E_INTERRUPTED && res != GNUTLS_E_AGAIN) return res;
74 if (res >= 0 || gnutls_error_is_fatal(res)) return res;
78 #else
79 # include "libpolarssl/ssl.h"
80 # include "libpolarssl/entropy.h"
81 # include "libpolarssl/ctr_drbg.h"
83 typedef struct {
84 //havege_state hs;
85 ssl_context ssl;
86 ssl_session ssn;
88 entropy_context ssl_entropy_cli;
89 ctr_drbg_context ssl_ctr_drbg_cli;
90 //entropy_context ssl_entropy_cli, ssl_entropy_srv;
91 //ctr_drbg_context ssl_ctr_drbg_cli, ssl_ctr_drbg_srv;
92 //pk_context ssl_pkey;
93 //rsa_context ssl_rsa;
94 } TSySSLInfo;
97 static int SyTCP_SSLSend (void *sock, const unsigned char *buf, size_t len) {
98 int res;
99 if (len < 1) return 0;
100 res = send(((TSySocket *)sock)->fd, (void *)buf, len, SY_SOCK_MSG_NO_SIGNAL);
101 if (!res) res = -666;
102 return res;
105 static int SyTCP_SSLRecv (void *sock, unsigned char *buf, size_t len) {
106 int res;
108 if (len < 1) return 0;
109 /*fprintf(stderr, "\r*** SyTCP_SSLRecv: %i \n", len);*/
110 res = recv(((TSySocket *)sock)->fd, (void *)buf, len, 0);
111 /*fprintf(stderr, "\r*** SyTCP_SSLRecv result: %i \n", res);*/
112 if (!res) res = -666;
113 return res;
115 #endif /* polarssl */
117 #endif
120 static int SyTCPLastError (void) {
121 return errno;
125 /* returns allocated string or NULL */
126 char *SyTCPGetLastErrorMsg (TSySocket *fd) {
127 char mbuf[1024], *estr;
129 #ifdef SY_XYZ_TMP
130 #undef SY_XYZ_TMP
131 #endif
133 #ifdef _GNU_SOURCE
134 #define SY_XYZ_TMP
135 #else
136 #if (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600)
137 #else
138 #define SY_XYZ_TMP
139 #endif
140 #endif /* _GNU_SOURCE */
142 if (!fd || !fd->errCode) return NULL;
143 memset(mbuf, 0, sizeof(mbuf));
144 estr = strerror_r(fd->errCode, mbuf, sizeof(mbuf));
145 #ifdef SY_XYZ_TMP
146 /* GNU */
147 return SyStrNew(estr, -1);
148 #else
149 /* XSI-compliant */
150 if (!estr) return SySPrintf("(%i) %s", fd->errCode, mbuf);
151 return NULL;
152 #endif /* XSI test */
154 #ifdef SY_XYZ_TMP
155 #undef SY_XYZ_TMP
156 #endif
160 TSyResult SyGetIFIP (char *ip, const char *iface) {
161 int res, fd;
162 struct ifreq ifr;
163 struct in_addr inp;
164 if (!ip) return SY_ERROR;
165 *ip = '\0';
166 if (!iface || !(*iface)) return SY_ERROR;
168 if (inet_aton(iface, &inp)) {
169 strcpy(ip, inet_ntoa(inp));
170 return SY_OK;
173 if (strlen(iface) > IFNAMSIZ) return SY_ERROR;
174 fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
175 memset(&ifr, 0, sizeof(struct ifreq));
176 strcpy(ifr.ifr_name, iface);
177 ifr.ifr_addr.sa_family = AF_INET;
178 res = ioctl(fd, SIOCGIFADDR, &ifr);
179 SySocketClose(fd);
180 if (!res) {
181 struct sockaddr_in *x = (struct sockaddr_in *)&ifr.ifr_addr;
182 strcpy(ip, inet_ntoa(x->sin_addr));
183 return SY_OK;
185 return SY_ERROR;
189 TSyResult SyTCPInitSocket (TSySocket *fd) {
190 #ifdef SYOPT_ALLOW_HTTPS
191 memset(fd, 0, sizeof(TSySocket));
192 /*fd->usessl = 0;*/
193 #endif
194 fd->errCode = 0;
195 fd->fd = -1;
196 return SY_OK;
200 #ifdef SYOPT_ALLOW_HTTPS
201 TSyResult SyTCPInitSSL (TSySocket *fd, const char *hostname, int timeout, const TSyPrintStr *pfn) {
202 TSySSLInfo *si;
204 fd->errCode = 0;
205 if (fd->fd < 0) return SY_ERROR;
206 if (fd->usessl) return SY_ERROR;
208 if (!fd->sslinfo) {
209 fd->sslinfo = calloc(1, sizeof(TSySSLInfo));
210 if (!fd->sslinfo) return SY_ERROR;
212 si = fd->sslinfo;
214 #ifdef SY_USE_GNUTLS
215 if (!gnutlsInited) {
216 if (gnutls_global_init() < 0) return SY_ERROR;
217 gnutlsInited = 1;
220 if (gnutls_certificate_allocate_credentials(&si->xcred) < 0) return SY_ERROR;
222 // initialize TLS session
223 if (gnutls_init(&si->session, GNUTLS_CLIENT) < 0) {
224 gnutls_certificate_free_credentials(si->xcred);
225 return SY_ERROR;
228 fd->usessl = 1;
230 #if 0
231 const char *err = NULL;
232 //if (gnutls_priority_set_direct(si->session, "PERFORMANCE", &err) < 0) return SY_ERROR;
233 if (gnutls_priority_set_direct(si->session, "SECURE256", &err) < 0) return SY_ERROR;
234 #else
235 if (gnutls_set_default_priority(si->session) < 0) return SY_ERROR;
236 gnutls_session_enable_compatibility_mode(si->session);
237 #endif
239 if (hostname) {
240 int res = gnutls_server_name_set(si->session, GNUTLS_NAME_DNS, hostname, strlen(hostname));
241 if (res < 0) {
242 SyMessage(pfn, SY_MSG_ERROR, " GnuTLS error (while setting hostname '%s') %d: %s", res, hostname, gnutls_strerror(res));
243 return SY_ERROR;
247 // put the x509 credentials to the current session
248 if (gnutls_credentials_set(si->session, GNUTLS_CRD_CERTIFICATE, si->xcred) < 0) return SY_ERROR;
250 // pass the socket handle off to gnutls
251 gnutls_transport_set_int(si->session, fd->fd);
253 gnutls_handshake_set_timeout(si->session, (timeout < 0 ? GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT : timeout*1000));
255 // perform the TLS handshake
256 for (;;) {
257 int res = gnutls_handshake(si->session);
258 //fprintf(stderr, "...handshacking: res=%d (%d) (to=%d)\n", res, gnutls_error_is_fatal(res), timeout);
259 if (res >= 0) break;
260 if (gnutls_error_is_fatal(res)) {
261 SyMessage(pfn, SY_MSG_ERROR, " GnuTLS error %d: %s", res, gnutls_strerror(res));
262 if (res == GNUTLS_E_WARNING_ALERT_RECEIVED || res == GNUTLS_E_FATAL_ALERT_RECEIVED) {
263 gnutls_alert_description_t alt = gnutls_alert_get(si->session);
264 const char *astr = gnutls_alert_get_name(alt);
265 SyMessage(pfn, SY_MSG_ERROR, " GnuTLS alert: %s", astr);
267 return SY_ERROR;
272 char *desc = gnutls_session_get_desc(si->session);
273 SyMessage(pfn, SY_MSG_NOTICE, " GnuTLS session info: %s", desc);
274 gnutls_free(desc);
276 #else
277 const char *pers0 = "fuckme";
278 //static const char *pers1 = "fuckhim";
280 //havege_init(&(si->hs));
281 memset(&(si->ssn), 0, sizeof(ssl_session));
283 entropy_init(&si->ssl_entropy_cli);
284 if (ctr_drbg_init(&si->ssl_ctr_drbg_cli, entropy_func, &si->ssl_entropy_cli, (const void *)pers0, strlen(pers0)) != 0) {
285 //fprintf(stderr, "FUCKED: ctr_drbg_init(cli) returned %d\n", ret);
286 free(fd->sslinfo);
287 fd->sslinfo = NULL;
288 return SY_ERROR;
291 if (ssl_init(&(si->ssl))) return SY_ERROR;
292 /*ssl_set_debuglvl(&(si->ssl), 0);*/
293 ssl_set_endpoint(&(si->ssl), SSL_IS_CLIENT);
294 ssl_set_authmode(&(si->ssl), SSL_VERIFY_NONE); /*!!!*/
295 ssl_set_rng(&(si->ssl), ctr_drbg_random, &(si->ssl_ctr_drbg_cli));
296 ssl_set_bio(&(si->ssl), SyTCP_SSLRecv, (void *)fd, SyTCP_SSLSend, (void *)fd);
297 //ssl_set_ciphers(&(si->ssl), ssl_default_ciphers);
299 //ssl_set_session(&(si->ssl), 1, timeout, &(si->ssn));
300 #endif
302 fd->usessl = 1;
303 return SY_OK;
305 #endif
308 TSyResult SyTCPCloseSocket (TSySocket *fd) {
309 if (fd->fd >= 0) {
310 #ifdef SYOPT_ALLOW_HTTPS
311 if (fd->usessl && fd->sslinfo) {
312 TSySSLInfo *si = fd->sslinfo;
313 #ifdef SY_USE_GNUTLS
314 if (fd->usessl) {
315 gnutls_deinit(si->session);
316 gnutls_certificate_free_credentials(si->xcred);
318 #else
319 if (fd->usessl > 1) ssl_close_notify(&(si->ssl));
320 ssl_free(&(si->ssl));
321 memset(&(si->ssl), 0, sizeof(si->ssl));
322 #endif
324 fd->usessl = 0;
325 if (fd->sslinfo) {
326 memset(fd->sslinfo, 0, sizeof(TSySSLInfo));
327 free(fd->sslinfo);
328 fd->sslinfo = NULL;
330 #endif
331 SySocketClose(fd->fd);
332 fd->fd = -1;
334 return SY_OK;
338 TSyResult SyTCPConnect (TSySocket *fd, char *hostname, int port, const char *iface, int timeout, const TSyPrintStr *pfn) {
339 struct hostent *host = NULL;
340 struct sockaddr_in addr;
341 struct sockaddr_in local;
342 char ifip[64];
343 int val, size;
344 struct timeval tv;
346 SyMessage(pfn, SY_MSG_NOTICE, "connecting to %s:%i", hostname, port);
348 SyTCPInitSocket(fd);
349 if (!hostname || !(*hostname)) return SY_ERROR;
350 if (iface && *iface && strcmp(iface, "-")) {
351 SyMessage(pfn, SY_MSG_NOTICE, " resolving local intefrace %s...\n", iface);
352 if (SyGetIFIP(ifip, iface) != SY_OK) {
353 SyMessage(pfn, SY_MSG_ERROR, "can't determine interface IP for %s", iface);
354 return SY_ERROR;
356 } else *ifip = '\0';
358 SyMessage(pfn, SY_MSG_NOTICE, " resolving %s", hostname);
359 host = gethostbyname(hostname);
360 if (!host || !host->h_name || !*host->h_name) {
361 fd->errCode = SyTCPLastError();
362 SyMessage(pfn, SY_MSG_ERROR, "can't resolve %s", hostname);
363 return SY_ERROR;
366 fd->fd = socket(AF_INET, SOCK_STREAM, 0);
367 if (fd->fd == -1) {
368 fd->errCode = SyTCPLastError();
369 SyMessage(pfn, SY_MSG_ERROR, "can't create socket");
370 return SY_ERROR;
372 if (*ifip) {
373 SyMessage(pfn, SY_MSG_NOTICE, " binding to local intefrace [%s]...\n", ifip);
374 local.sin_family = AF_INET;
375 local.sin_port = 0;
376 local.sin_addr.s_addr = inet_addr(ifip);
377 if (bind(fd->fd, (struct sockaddr *)&local, sizeof(struct sockaddr_in)) == -1) {
378 fd->errCode = SyTCPLastError();
379 SyTCPCloseSocket(fd);
380 SyMessage(pfn, SY_MSG_ERROR, "can't bind to local interface %s", ifip);
381 return SY_ERROR;
385 addr.sin_family = AF_INET;
386 addr.sin_port = htons(port);
387 addr.sin_addr = *((struct in_addr *)host->h_addr);
388 if (*ifip) SyMessage(pfn, SY_MSG_NOTICE, " connecting to %s:%i (%s)", inet_ntoa(addr.sin_addr), port, ifip);
389 else SyMessage(pfn, SY_MSG_NOTICE, " connecting to %s:%i", inet_ntoa(addr.sin_addr), port);
391 if (timeout > 0) {
392 tv.tv_sec = timeout; tv.tv_usec = 0; size = sizeof(tv);
393 if (setsockopt(fd->fd, SOL_SOCKET, SO_RCVTIMEO, &tv, size)) {
394 SyMessage(pfn, SY_MSG_WARNING, " can't set receive timeout");
396 tv.tv_sec = timeout; tv.tv_usec = 0; size = sizeof(tv);
397 if (setsockopt(fd->fd, SOL_SOCKET, SO_SNDTIMEO, &tv, size)) {
398 SyMessage(pfn, SY_MSG_WARNING, " can't set send timeout");
400 val = 1; size = sizeof(val);
401 setsockopt(fd->fd, SOL_SOCKET, SO_KEEPALIVE, &val, size);
404 if (connect(fd->fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0) {
405 fd->errCode = SyTCPLastError();
406 SyTCPCloseSocket(fd);
407 SyMessage(pfn, SY_MSG_ERROR, "can't connect to %s:%i", inet_ntoa(addr.sin_addr), port);
408 return SY_ERROR;
410 SyMessage(pfn, SY_MSG_NOTICE, " connected to %s:%i", inet_ntoa(addr.sin_addr), port);
412 return SY_OK;
417 TSyResult SyTCPSend (TSySocket *fd, const void *buf, int bufSize) {
418 const char *c = (char *)buf;
420 fd->errCode = 0;
421 if (!buf) return SY_OK;
422 while (bufSize > 0) {
423 #ifdef SYOPT_ALLOW_HTTPS
424 int wr, tosend;
425 if (fd->usessl) {
426 tosend = (bufSize > SSL_MAX_CONTENT_LEN ? SSL_MAX_CONTENT_LEN : bufSize);
427 #ifdef SY_USE_GNUTLS
428 wr = SyTCP_SSLSend((TSySSLInfo *)fd->sslinfo, buf, tosend);
429 #else
430 wr = ssl_write(&(((TSySSLInfo *)fd->sslinfo)->ssl), (void *)buf, tosend);
431 if (wr == -666) wr = 0;
432 if (wr != POLARSSL_ERR_NET_WANT_WRITE) {
433 if (wr >= 0) fd->usessl = 2;
434 } else {
435 wr = -1;
437 #endif
438 } else {
439 wr = send(fd->fd, c, bufSize, SY_SOCK_MSG_NO_SIGNAL);
441 #else
442 int wr = send(fd->fd, c, bufSize, SY_SOCK_MSG_NO_SIGNAL);
443 #endif
444 if (wr <= 0) {
445 fd->errCode = SyTCPLastError();
446 return SY_ERROR;
448 c += wr; bufSize -= wr;
450 return SY_OK;
454 TSyResult SyTCPSendStr (TSySocket *fd, const char *str) {
455 if (!fd || fd->fd < 0) return SY_ERROR;
456 if (!str || !(*str)) return SY_OK;
457 return SyTCPSend(fd, str, strlen(str));
461 /* <0: received (-res) bytes; read error */
462 int SyTCPReceiveEx (TSySocket *fd, void *buf, int bufSize, int allowPartial) {
463 char *c = (char *)buf;
464 int total = 0;
466 /*fprintf(stderr, "\r*** SyTCPReceiveEx: %i (%i) \n", bufSize, allowPartial);*/
467 fd->errCode = 0;
468 if (fd->fd < 0) return 0;
469 while (bufSize > 0) {
470 #ifdef SYOPT_ALLOW_HTTPS
471 int rd, toread;
472 if (fd->usessl) {
473 toread = (bufSize > SSL_MAX_CONTENT_LEN ? SSL_MAX_CONTENT_LEN : bufSize);
474 /*fprintf(stderr, "\r*** reading: %i \n", toread);*/
475 #ifdef SY_USE_GNUTLS
476 rd = SyTCP_SSLRecv((TSySSLInfo *)fd->sslinfo, buf, toread);
477 #else
478 rd = ssl_read(&(((TSySSLInfo *)fd->sslinfo)->ssl), buf, toread);
479 /*fprintf(stderr, "\r*** rd=%i \n", rd);*/
480 if (rd == -666) rd = 0;
481 if (rd != POLARSSL_ERR_NET_WANT_READ) {
482 if (rd >= 0) fd->usessl = 2;
483 } else {
484 rd = -1;
486 #endif
487 } else {
488 rd = recv(fd->fd, c, bufSize, 0);
490 #else
491 int rd = recv(fd->fd, c, bufSize, 0);
492 #endif
493 if (rd <= 0) fd->errCode = SyTCPLastError();
494 if (!rd) return total;
495 if (rd < 0) return -total;
496 c += rd; total += rd; bufSize -= rd;
497 if (allowPartial) break;
499 return total;
503 int SyTCPReceive (TSySocket *fd, void *buf, int bufSize) {
504 return SyTCPReceiveEx(fd, buf, bufSize, SY_TCP_DONT_ALLOWPARTIAL);
508 TSyResult SyTCPSendLine (const TSyPrintStr *pfn, int printLine, TSySocket *fd, const char *fmt, ...) {
509 TSyResult res;
510 int n, size = 100;
511 char *p, *np;
512 va_list ap;
514 if ((p = calloc(1, size+4)) == NULL) { SyMessage(pfn, SY_MSG_ERROR, "memory error"); return SY_ERROR; }
515 while (1) {
516 va_start(ap, fmt);
517 n = vsnprintf(p, size, fmt, ap);
518 va_end(ap);
519 if (n > -1 && n < size) break;
520 if (n > -1) size = n+1; else size *= 2;
521 if ((np = realloc(p, size+4)) == NULL) {
522 free(p);
523 SyMessage(pfn, SY_MSG_ERROR, "memory error");
524 return SY_ERROR;
526 p = np;
528 if (printLine) SyMessage(pfn, SY_MSG_NOTICE, " %s", p);
529 strcat(p, "\r\n");
530 res = SyTCPSendStr(fd, p);
531 free(p);
532 if (res != SY_OK) { SyMessage(pfn, SY_MSG_ERROR, "socket write error"); return SY_ERROR; }
533 return SY_OK;
537 char *SyTCPReceiveStrEx (TSySocket *fd, int maxSize, char *dest) {
538 char ch, *d, *chp;
539 int f, rcv;
541 fd->errCode = 0;
542 if (maxSize <= 0 || fd->fd < 0 || !dest) return NULL;
543 memset(dest, 0, maxSize+1);
544 d = dest;
545 while (maxSize) {
546 int rd;
547 #ifdef SYOPT_ALLOW_HTTPS
548 rd = 0;
549 #else
550 rd = recv(fd->fd, d, maxSize+2, MSG_PEEK);
551 #endif /* SYOPT_ALLOW_HTTPS */
552 if (!rd) {
553 rcv = 1;
554 #ifdef SYOPT_ALLOW_HTTPS
555 if (fd->usessl) {
556 #ifdef SY_USE_GNUTLS
557 rd = SyTCP_SSLRecv((TSySSLInfo *)fd->sslinfo, d, 1);
558 #else
559 rd = ssl_read(&(((TSySSLInfo *)fd->sslinfo)->ssl), (void *)d, 1);
560 if (rd == -666) rd = 0;
561 if (rd != POLARSSL_ERR_NET_WANT_READ) {
562 if (rd >= 0) fd->usessl = 2;
563 } else {
564 rd = -1;
566 #endif
567 } else
568 #endif
569 rd = recv(fd->fd, d, 1, 0);
570 } else {
571 rcv = 0;
573 if (rd <= 0) fd->errCode = SyTCPLastError();
574 if (rd < 0) return NULL;
575 if (rd == 0) break; /* connection closed */
576 /* check for end of headers */
577 chp = d; f = 0;
578 while (f < rd) {
579 ch = *chp; f++;
580 if (!ch) return NULL; /* can't got zero byte here */
581 if (ch == '\n') {
582 /* yes! all chars are here */
583 if (!rcv) {
584 /* receive peeked bytes */
585 rd = recv(fd->fd, d, f, 0);
586 if (rd < 0) return NULL;
588 if (chp > dest && *(chp-1) == '\r') chp--; /* skip CR */
589 *chp = '\0';
590 return dest;
592 chp++;
594 if (!rcv) {
595 /* receive peeked bytes */
596 rd = recv(fd->fd, d, rd, 0);
597 if (rd <= 0) return NULL;
599 maxSize -= rd; d += rd;
601 /* headers too big */
602 return NULL;
606 char *SyTCPReceiveStr (TSySocket *fd, int maxSize) {
607 char *dest, *res;
609 fd->errCode = 0;
610 if (maxSize <= 0 || fd->fd < 0) return NULL;
611 dest = calloc(1, maxSize+8);
612 if (!dest) return NULL;
613 res = SyTCPReceiveStrEx(fd, maxSize, dest);
614 if (!res) { free(dest); return NULL; }
615 return dest;
619 /* return NULL if string was too big or on error;
620 tries to receive all headers
622 char *SyTCPReceiveHdrs (TSySocket *fd, int maxSize) {
623 char ch, *d, *chp, *dest;
624 int f, l, rcv;
626 fd->errCode = 0;
627 if (maxSize <= 0 || fd->fd < 0) return NULL;
628 dest = calloc(1, maxSize+8);
629 d = dest;
630 while (maxSize) {
631 int rd;
632 #ifdef SYOPT_ALLOW_HTTPS
633 rd = 0;
634 #else
635 rd = recv(fd->fd, d, maxSize+2, MSG_PEEK);
636 #endif /* SYOPT_ALLOW_HTTPS */
637 if (!rd) {
638 rcv = 1;
639 #ifdef SYOPT_ALLOW_HTTPS
640 if (fd->usessl) {
641 #ifdef SY_USE_GNUTLS
642 rd = SyTCP_SSLRecv((TSySSLInfo *)fd->sslinfo, d, 1);
643 #else
644 rd = ssl_read(&(((TSySSLInfo *)fd->sslinfo)->ssl), (void *)d, 1);
645 if (rd == -666) rd = 0;
646 if (rd != POLARSSL_ERR_NET_WANT_READ) {
647 if (rd >= 0) fd->usessl = 2;
648 } else {
649 rd = -1;
651 #endif
652 } else
653 #endif
654 rd = recv(fd->fd, d, 1, 0);
655 } else rcv = 0;
656 if (rd <= 0) fd->errCode = SyTCPLastError();
657 if (rd < 0) {
658 free(dest);
659 return NULL;
661 if (rd == 0) break; /* connection closed */
662 /* check for end of headers */
663 chp = d; f = 0;
664 while (f < rd) {
665 ch = *chp; chp++; f++;
666 if (!ch) { free(dest); return NULL; } /* can't got zero byte here */
667 if (ch == '\n') {
668 /* check for empty line */
669 l = (chp-dest)-2; /* prev ch index */
670 if (l >= 0 && dest[l] == '\r') l--; /* skip CR */
671 if (l >= 0 && dest[l] == '\n') {
672 /* yes! all headers are here */
673 if (!rcv) {
674 /* receive peeked bytes */
675 rd = recv(fd->fd, d, f, 0);
676 if (rd < 0) { free(dest); return NULL; }
678 *chp = '\0';
679 return dest;
683 if (!rcv) {
684 /* receive peeked bytes */
685 rd = recv(fd->fd, d, rd, 0);
686 if (rd <= 0) { free(dest); return NULL; }
688 maxSize -= rd; d += rd;
690 /* headers too big */
691 free(dest);
692 return NULL;
696 #endif