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