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
28 #include "syren_tcp.h"
32 #define SY_SOCK_MSG_NO_SIGNAL SO_NOSIGPIPE
34 #define SY_SOCK_MSG_NO_SIGNAL MSG_NOSIGNAL
42 #ifdef SYOPT_ALLOW_HTTPS
45 # include <gnutls/gnutls.h>
46 # include <gnutls/x509.h>
48 #define SSL_MAX_CONTENT_LEN (32768)
49 static int gnutlsInited
= 0;
53 gnutls_certificate_credentials_t xcred
;
54 gnutls_session_t session
;
55 //int handshakeComplete;
58 static int SyTCP_SSLSend (TSySSLInfo
*si
, const void *buf
, size_t len
) {
59 if (len
< 1) return 0;
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;
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
;
79 # include "libpolarssl/ssl.h"
80 # include "libpolarssl/entropy.h"
81 # include "libpolarssl/ctr_drbg.h"
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;
97 static int SyTCP_SSLSend (void *sock
, const unsigned char *buf
, size_t len
) {
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;
105 static int SyTCP_SSLRecv (void *sock
, unsigned char *buf
, size_t len
) {
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;
115 #endif /* polarssl */
120 static int SyTCPLastError (void) {
125 /* returns allocated string or NULL */
126 char *SyTCPGetLastErrorMsg (TSySocket
*fd
) {
127 char mbuf
[1024], *estr
;
136 #if (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600)
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
));
147 return SyStrNew(estr
, -1);
150 if (!estr
) return SySPrintf("(%i) %s", fd
->errCode
, mbuf
);
152 #endif /* XSI test */
160 TSyResult
SyGetIFIP (char *ip
, const char *iface
) {
164 if (!ip
) return SY_ERROR
;
166 if (!iface
|| !(*iface
)) return SY_ERROR
;
168 if (inet_aton(iface
, &inp
)) {
169 strcpy(ip
, inet_ntoa(inp
));
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
);
181 struct sockaddr_in
*x
= (struct sockaddr_in
*)&ifr
.ifr_addr
;
182 strcpy(ip
, inet_ntoa(x
->sin_addr
));
189 TSyResult
SyTCPInitSocket (TSySocket
*fd
) {
190 #ifdef SYOPT_ALLOW_HTTPS
191 memset(fd
, 0, sizeof(TSySocket
));
200 #ifdef SYOPT_ALLOW_HTTPS
201 TSyResult
SyTCPInitSSL (TSySocket
*fd
, const char *hostname
, int timeout
, const TSyPrintStr
*pfn
) {
205 if (fd
->fd
< 0) return SY_ERROR
;
206 if (fd
->usessl
) return SY_ERROR
;
209 fd
->sslinfo
= calloc(1, sizeof(TSySSLInfo
));
210 if (!fd
->sslinfo
) return SY_ERROR
;
216 if (gnutls_global_init() < 0) return SY_ERROR
;
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
);
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
;
235 if (gnutls_set_default_priority(si
->session
) < 0) return SY_ERROR
;
236 gnutls_session_enable_compatibility_mode(si
->session
);
240 int res
= gnutls_server_name_set(si
->session
, GNUTLS_NAME_DNS
, hostname
, strlen(hostname
));
242 SyMessage(pfn
, SY_MSG_ERROR
, " GnuTLS error (while setting hostname '%s') %d: %s", res
, hostname
, gnutls_strerror(res
));
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
257 int res
= gnutls_handshake(si
->session
);
258 //fprintf(stderr, "...handshacking: res=%d (%d) (to=%d)\n", res, gnutls_error_is_fatal(res), timeout);
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
);
272 char *desc
= gnutls_session_get_desc(si
->session
);
273 SyMessage(pfn
, SY_MSG_NOTICE
, " GnuTLS session info: %s", desc
);
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);
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));
308 TSyResult
SyTCPCloseSocket (TSySocket
*fd
) {
310 #ifdef SYOPT_ALLOW_HTTPS
311 if (fd
->usessl
&& fd
->sslinfo
) {
312 TSySSLInfo
*si
= fd
->sslinfo
;
315 gnutls_deinit(si
->session
);
316 gnutls_certificate_free_credentials(si
->xcred
);
319 if (fd
->usessl
> 1) ssl_close_notify(&(si
->ssl
));
320 ssl_free(&(si
->ssl
));
321 memset(&(si
->ssl
), 0, sizeof(si
->ssl
));
326 memset(fd
->sslinfo
, 0, sizeof(TSySSLInfo
));
331 SySocketClose(fd
->fd
);
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
;
346 SyMessage(pfn
, SY_MSG_NOTICE
, "connecting to %s:%i", hostname
, port
);
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
);
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
);
366 fd
->fd
= socket(AF_INET
, SOCK_STREAM
, 0);
368 fd
->errCode
= SyTCPLastError();
369 SyMessage(pfn
, SY_MSG_ERROR
, "can't create socket");
373 SyMessage(pfn
, SY_MSG_NOTICE
, " binding to local intefrace [%s]...\n", ifip
);
374 local
.sin_family
= AF_INET
;
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
);
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
);
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
);
410 SyMessage(pfn
, SY_MSG_NOTICE
, " connected to %s:%i", inet_ntoa(addr
.sin_addr
), port
);
417 TSyResult
SyTCPSend (TSySocket
*fd
, const void *buf
, int bufSize
) {
418 const char *c
= (char *)buf
;
421 if (!buf
) return SY_OK
;
422 while (bufSize
> 0) {
423 #ifdef SYOPT_ALLOW_HTTPS
426 tosend
= (bufSize
> SSL_MAX_CONTENT_LEN
? SSL_MAX_CONTENT_LEN
: bufSize
);
428 wr
= SyTCP_SSLSend((TSySSLInfo
*)fd
->sslinfo
, buf
, tosend
);
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;
439 wr
= send(fd
->fd
, c
, bufSize
, SY_SOCK_MSG_NO_SIGNAL
);
442 int wr
= send(fd
->fd
, c
, bufSize
, SY_SOCK_MSG_NO_SIGNAL
);
445 fd
->errCode
= SyTCPLastError();
448 c
+= wr
; bufSize
-= wr
;
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
;
466 /*fprintf(stderr, "\r*** SyTCPReceiveEx: %i (%i) \n", bufSize, allowPartial);*/
468 if (fd
->fd
< 0) return 0;
469 while (bufSize
> 0) {
470 #ifdef SYOPT_ALLOW_HTTPS
473 toread
= (bufSize
> SSL_MAX_CONTENT_LEN
? SSL_MAX_CONTENT_LEN
: bufSize
);
474 /*fprintf(stderr, "\r*** reading: %i \n", toread);*/
476 rd
= SyTCP_SSLRecv((TSySSLInfo
*)fd
->sslinfo
, buf
, toread
);
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;
488 rd
= recv(fd
->fd
, c
, bufSize
, 0);
491 int rd
= recv(fd
->fd
, c
, bufSize
, 0);
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;
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
, ...) {
514 if ((p
= calloc(1, size
+4)) == NULL
) { SyMessage(pfn
, SY_MSG_ERROR
, "memory error"); return SY_ERROR
; }
517 n
= vsnprintf(p
, size
, fmt
, 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
) {
523 SyMessage(pfn
, SY_MSG_ERROR
, "memory error");
528 if (printLine
) SyMessage(pfn
, SY_MSG_NOTICE
, " %s", p
);
530 res
= SyTCPSendStr(fd
, p
);
532 if (res
!= SY_OK
) { SyMessage(pfn
, SY_MSG_ERROR
, "socket write error"); return SY_ERROR
; }
537 char *SyTCPReceiveStrEx (TSySocket
*fd
, int maxSize
, char *dest
) {
542 if (maxSize
<= 0 || fd
->fd
< 0 || !dest
) return NULL
;
543 memset(dest
, 0, maxSize
+1);
547 #ifdef SYOPT_ALLOW_HTTPS
550 rd
= recv(fd
->fd
, d
, maxSize
+2, MSG_PEEK
);
551 #endif /* SYOPT_ALLOW_HTTPS */
554 #ifdef SYOPT_ALLOW_HTTPS
557 rd
= SyTCP_SSLRecv((TSySSLInfo
*)fd
->sslinfo
, d
, 1);
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;
569 rd
= recv(fd
->fd
, d
, 1, 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 */
580 if (!ch
) return NULL
; /* can't got zero byte here */
582 /* yes! all chars are here */
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 */
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 */
606 char *SyTCPReceiveStr (TSySocket
*fd
, int maxSize
) {
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
; }
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
;
627 if (maxSize
<= 0 || fd
->fd
< 0) return NULL
;
628 dest
= calloc(1, maxSize
+8);
632 #ifdef SYOPT_ALLOW_HTTPS
635 rd
= recv(fd
->fd
, d
, maxSize
+2, MSG_PEEK
);
636 #endif /* SYOPT_ALLOW_HTTPS */
639 #ifdef SYOPT_ALLOW_HTTPS
642 rd
= SyTCP_SSLRecv((TSySSLInfo
*)fd
->sslinfo
, d
, 1);
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;
654 rd
= recv(fd
->fd
, d
, 1, 0);
656 if (rd
<= 0) fd
->errCode
= SyTCPLastError();
661 if (rd
== 0) break; /* connection closed */
662 /* check for end of headers */
665 ch
= *chp
; chp
++; f
++;
666 if (!ch
) { free(dest
); return NULL
; } /* can't got zero byte here */
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 */
674 /* receive peeked bytes */
675 rd
= recv(fd
->fd
, d
, f
, 0);
676 if (rd
< 0) { free(dest
); return NULL
; }
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 */