1 /* This program is free software; you can redistribute it and/or modify
2    it under the terms of the GNU General Public License as published by
3    the Free Software Foundation; version 2 dated June, 1991.
4 
5    This program is distributed in the hope that it will be useful,
6    but WITHOUT ANY WARRANTY; without even the implied warranty of
7    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
8    GNU General Public License for more details.
9 
10    You should have received a copy of the GNU General Public License
11    along with this program;  if not, write to the Free Software
12    Foundation, Inc., 675 Mass Ave., Cambridge, MA 02139, USA. */
13 
14 #include <stdio.h>
15 #include <fcntl.h>
16 #include <string.h>
17 #include <sys/time.h>
18 #include <sys/types.h>
19 #include <unistd.h>
20 #include <sys/socket.h>
21 #include <netdb.h>
22 #include <netinet/in.h>
23 #ifdef HAVE_OPENSSL_SSL_H
24 #include <openssl/ssl.h>
25 #include <openssl/bio.h>
26 #endif
27 #ifdef __QNX__
28 #include <sys/select.h> /* for select() et. al. */
29 #endif
30 
31 #include "config.h"
32 #include "socklib.h"
33 
34 #define S_LIBRARY
35 #define MG_SOCKLIB_C
36 
37 /* This source code was originally based on
38 
39     "The Linux A-Z" by Phil Cornes,
40     chapter 18 - "Tiny Socket Library"
41     ISBN 0-13-234709-1
42 
43    The original source has been modified by
44    Martin Domig <martin.domig@gmx.net>			*/
45 
46 
Sopen(void)47 SOCKET *Sopen(void) {
48    SOCKET *sp;
49 
50    if ((sp = (SOCKET *)malloc(sizeof(SOCKET))) == NULL)
51       return 0;
52 
53    /* Create a socket and store its descriptor in sp->sd. Declarations:
54       AF_INET.......ARPA Internet protocol
55       SOCK_STREAM...TCP-type connection
56       0.............IP Protocol Type
57       See socket(2) for details.											*/
58    if ((sp->sd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
59       free(sp);	/* Oops, we got no socket! Free the mem and return error	*/
60       return 0;
61    }
62 
63    sp->sinlen = sizeof(sp->sin);
64    sp->bindflag = SOCKET_RESET;
65 
66 #ifdef HAVE_OPENSSL_SSL_H
67    /* ssl is initialized after the tcp connection is opened */
68    sp->ssl = NULL;
69    sp->sslBufSize = 0;
70 #endif
71 
72    return sp;
73 }
74 
Sclose(SOCKET * sp)75 int Sclose(SOCKET *sp) {
76    int sd;
77 
78    if (!sp)
79       return -1;
80 
81    sd = sp->sd;
82 #ifdef HAVE_OPENSSL_SSL_H
83    if (sp->ssl) {
84       SSL_shutdown(sp->ssl);
85       SSL_free(sp->ssl);
86    }
87 
88    if (sp->sslBufSize) {
89       free(sp->sslBufMalloc);
90       sp->sslBufSize = 0;
91    }
92 #endif
93 
94    free(sp);
95    return close(sd);
96 }
97 
Sserver(SOCKET * sp,int port,int sync)98 int Sserver(SOCKET *sp, int port, int sync) {
99    int flags;
100    struct hostent *hostent;
101    /* struct hostent {
102       	char	*h_name;        official name of host
103         char    **h_aliases;    alias list
104         int     h_addrtype;     host address type
105         int     h_length;       length of address
106         char    **h_addr_list;  list of addresses
107       }																		*/
108    char localhost[HOST_NAMELEN+1];
109 
110    /* We have to bind an address to the socket and then set up a queue
111       to listen for connection requests. This should only be done the first
112       time sserver() is called, and this is what sp->bindflag is used for.	*/
113    if (sp->bindflag == SOCKET_RESET) {	/* Bind server to localhost				*/
114       if (gethostname(localhost, HOST_NAMELEN) == -1
115          || (hostent = gethostbyname(localhost)) == 0)
116          return -1;
117 
118       sp->sin.sin_family = (short)hostent->h_addrtype;
119       sp->sin.sin_port = htons((unsigned short)port);
120       sp->sin.sin_addr.s_addr = *(unsigned long *)hostent->h_addr;
121 
122       if (bind(sp->sd, (struct sockaddr *)&sp->sin, sp->sinlen) == -1
123          || listen(sp->sd, 5) == -1)
124          return -1;
125 
126       sp->bindflag = SOCKET_SET;
127           					/* All done and OK. Never bind this one again.	*/
128    }
129 
130    switch (sync) {
131       case SOCKET_DELAY:
132          if ((flags = fcntl(sp->sd, F_GETFL)) == -1
133             || fcntl(sp->sd, F_SETFL, flags & ~O_NDELAY) == -1)
134             return -1;
135          break;
136       case SOCKET_NDELAY:
137          if ((flags = fcntl(sp->sd, F_GETFL)) == -1
138             || fcntl(sp->sd, F_SETFL, flags | O_NDELAY) == -1)
139             return -1;
140          break;
141       default:
142          return -1;
143    }
144 
145    return accept(sp->sd, (struct sockaddr *)&sp->sin, &sp->sinlen);
146 }
147 
148 /* sclient tries to connect to a specified server on a given machine.
149    The function waits until a connection with the server is established and
150    then returns a socket descriptor connected to the server, -1 on error.	*/
Sclient(SOCKET * sp,char * name,int port)151 int Sclient(SOCKET *sp, char *name, int port) {
152    struct hostent *hostent;
153 
154    if ((hostent = gethostbyname(name)) == 0)
155       return -1;
156 
157    sp->sin.sin_family = (short)hostent->h_addrtype;
158    sp->sin.sin_port = htons((unsigned short)port);
159    sp->sin.sin_addr.s_addr = *(unsigned long *)hostent->h_addr;
160 
161    if (connect(sp->sd, (struct sockaddr *)&sp->sin, sp->sinlen) == -1)
162       return -1;
163 
164    return sp->sd;
165 }
166 
167 #ifdef HAVE_OPENSSL_SSL_H
168 /* modify an open socket to use SSL */
Sslclient(SOCKET * sp,char * trustedCaDir)169 int Sslclient(SOCKET *sp, char *trustedCaDir)
170 {
171    SSL_METHOD *meth;
172    SSL_CTX *ctx;
173    BIO *sbio;
174    X509 *peerCert;
175 
176    /* assert(sp contains an open socket) */
177 
178    SSL_library_init();
179 
180    meth = TLSv1_method();
181    ctx = SSL_CTX_new(meth);
182    if (!ctx)
183       return -1;
184 
185    sp->ssl = SSL_new(ctx);
186    if (sp->ssl == NULL)
187       return -1;
188 
189    /* don't check return value
190     * in case of error, verification will fail later on */
191    SSL_CTX_load_verify_locations(ctx, NULL, trustedCaDir);
192 
193    sbio = BIO_new_socket(sp->sd, BIO_NOCLOSE);
194    if (!sbio) {
195       return -1;
196    }
197 
198    SSL_set_bio(sp->ssl, sbio, sbio); /* read, write bio */
199 
200    if (SSL_connect(sp->ssl) != 1) {
201       return -1;
202    }
203 
204    peerCert = SSL_get_peer_certificate(sp->ssl);
205    if (!peerCert) {
206       return -1;
207    }
208 
209    X509_free(peerCert);
210 
211    if(SSL_get_verify_result(sp->ssl) != X509_V_OK) {
212       printf("asmail warning: could not verify server certificate\n");
213    }
214 
215    return sp->sd;
216 }
217 #endif
218 
219 
220 /* Read a maximum of n-1 characters from sd until a newline or
221    a '\0' is received. string stores the input WITHOUT a ter-
222    minating newline. Returns number of read characters, without
223    terminating \0.
224    HINT: pass the SIZE of the array as parameter for n.		 */
225 
Sread(int sd,char * string,int n,int timeout)226 size_t Sread(int sd, char *string, int n, int timeout)
227 {
228    fd_set rfds;
229    struct timeval tv;
230    int ret, rcd, i;
231 
232 /* See man 3 select for details ;)
233 
234 NOTES
235        Some  code calls select with all three sets empty, n zero,
236        and a non-null timeout as a fairly portable way  to  sleep
237        with subsecond precision.
238 
239        On  Linux,  timeout  is  modified to reflect the amount of
240        time not slept; most other implementations do not do this.
241        This  causes  problems  both  when  Linux code which reads
242        timeout is ported to other  operating  systems,  and  when
243        code  is  ported to Linux that reuses a struct timeval for
244        multiple selects in  a  loop  without  reinitializing  it.
245        Consider timeout to be undefined after select returns. */
246 
247    FD_ZERO(&rfds);
248    FD_SET(sd, &rfds);
249    i = 0;
250    n--;
251 
252    do {
253       tv.tv_sec = timeout;
254       tv.tv_usec = 0;
255 
256       ret = select(sd+1, &rfds, NULL, NULL, &tv);
257       if (ret > 0 && (!(rcd = read(sd, &string[i++], 1)))) {
258             string[i] = '\0';
259             return 0;
260       }
261    } while (ret > 0 && rcd > 0 && string[i-1] != 13 && string[i-1] && i < n);
262 
263    if (ret < 0 || rcd < 0) {
264       string[--i] = '\0';
265       return -1;
266    }
267 
268    if (string[i-1] == 13)
269       read(sd, &string[--i], 1); /* read 10 from sd */
270 
271    string[i] = '\0';
272    return i;
273 }
274 
Swrite(int sd,char * string)275 size_t Swrite(int sd, char *string)
276 {
277    return write(sd, string, strlen(string));
278 }
279 
280 
281 #ifdef HAVE_OPENSSL_SSL_H
282 /* behaves exactly like Sread() above */
Sslread(SOCKET * s,char * string,int n,int timeout)283 size_t Sslread(SOCKET *s, char *string, int n, int timeout)
284 {
285    size_t sizeRead;
286    fd_set rfds;
287    struct timeval tv;
288    int ret;
289 
290    if (s->ssl == NULL)
291       return -1;
292 
293    if (!s->sslBufSize) {
294       /* empty buffer: read from SSL */
295 
296       FD_ZERO(&rfds);
297       FD_SET(s->sd, &rfds);
298 
299       tv.tv_sec = timeout;
300       tv.tv_usec = 0;
301 
302       ret = select((s->sd)+1, &rfds, NULL, NULL, &tv);
303       if (ret > 0) {
304          s->sslBufMalloc = malloc(BUFSIZ * sizeof(char));
305          s->sslBuf = s->sslBufMalloc;
306          if (!s->sslBuf)
307             return -1;
308          s->sslBufSize = SSL_read(s->ssl, s->sslBuf, BUFSIZ);
309          if (s->sslBufSize <= 0) {
310             free(s->sslBufMalloc);
311             return -1;
312          }
313       }
314       else
315          return 0;
316    }
317 
318    /* read from buffer */
319    sizeRead = 0;
320    while (s->sslBufSize-- && (sizeRead++ < n-1)) {
321       if ( ((*string++ = *(s->sslBuf)++) == 0x0d) &&
322             (*(s->sslBuf)++ == 0x0a) ) {
323          s->sslBufSize--;     /* for the 0x0a we just read */
324          string--;   /* undo 0x0d in target string */
325          sizeRead--;
326          break;
327       }
328    }
329 
330    if (!s->sslBufSize)
331       free(s->sslBufMalloc);
332 
333    *string = 0x0;
334    return sizeRead;
335 }
336 
Sslwrite(SOCKET * s,char * string)337 size_t Sslwrite(SOCKET *s, char *string)
338 {
339    if (s->ssl == NULL)
340       return -1;
341 
342    return SSL_write(s->ssl, string, strlen(string));
343 }
344 #endif
345 
346