File: socklib.c

package info (click to toggle)
asmail 2.1-2
  • links: PTS
  • area: main
  • in suites: lenny
  • size: 1,024 kB
  • ctags: 301
  • sloc: ansic: 3,045; sh: 183; makefile: 88
file content (345 lines) | stat: -rw-r--r-- 9,006 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
/* This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 dated June, 1991.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program;  if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave., Cambridge, MA 02139, USA. */

#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#ifdef HAVE_OPENSSL_SSL_H 
#include <openssl/ssl.h>
#include <openssl/bio.h>
#endif
#ifdef __QNX__
#include <sys/select.h> /* for select() et. al. */
#endif

#include "config.h"
#include "socklib.h"

#define S_LIBRARY
#define MG_SOCKLIB_C

/* This source code was originally based on

    "The Linux A-Z" by Phil Cornes,
    chapter 18 - "Tiny Socket Library"
    ISBN 0-13-234709-1
    
   The original source has been modified by
   Martin Domig <martin.domig@gmx.net>			*/


SOCKET *Sopen(void) {
   SOCKET *sp;
   
   if ((sp = (SOCKET *)malloc(sizeof(SOCKET))) == NULL)
      return 0;
   
   /* Create a socket and store its descriptor in sp->sd. Declarations:
      AF_INET.......ARPA Internet protocol
      SOCK_STREAM...TCP-type connection
      0.............IP Protocol Type
      See socket(2) for details.											*/
   if ((sp->sd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
      free(sp);	/* Oops, we got no socket! Free the mem and return error	*/
      return 0;
   }
   
   sp->sinlen = sizeof(sp->sin);
   sp->bindflag = SOCKET_RESET;

#ifdef HAVE_OPENSSL_SSL_H 
   /* ssl is initialized after the tcp connection is opened */
   sp->ssl = NULL;
   sp->sslBufSize = 0;
#endif
   
   return sp;
}

int Sclose(SOCKET *sp) {
   int sd;

   if (!sp)
      return -1;
   
   sd = sp->sd;
#ifdef HAVE_OPENSSL_SSL_H 
   if (sp->ssl) {
      SSL_shutdown(sp->ssl);
      SSL_free(sp->ssl);
   }

   if (sp->sslBufSize) {
      free(sp->sslBufMalloc);
      sp->sslBufSize = 0;
   }
#endif

   free(sp);
   return close(sd);
}

int Sserver(SOCKET *sp, int port, int sync) {
   int flags;
   struct hostent *hostent;
   /* struct hostent {
      	char	*h_name;        official name of host
        char    **h_aliases;    alias list
        int     h_addrtype;     host address type
        int     h_length;       length of address
        char    **h_addr_list;  list of addresses
      }																		*/
   char localhost[HOST_NAMELEN+1];

   /* We have to bind an address to the socket and then set up a queue
      to listen for connection requests. This should only be done the first
      time sserver() is called, and this is what sp->bindflag is used for.	*/   
   if (sp->bindflag == SOCKET_RESET) {	/* Bind server to localhost				*/
      if (gethostname(localhost, HOST_NAMELEN) == -1
         || (hostent = gethostbyname(localhost)) == 0)
         return -1;
   
      sp->sin.sin_family = (short)hostent->h_addrtype;
      sp->sin.sin_port = htons((unsigned short)port);
      sp->sin.sin_addr.s_addr = *(unsigned long *)hostent->h_addr;
   
      if (bind(sp->sd, (struct sockaddr *)&sp->sin, sp->sinlen) == -1
         || listen(sp->sd, 5) == -1)
         return -1;
   
      sp->bindflag = SOCKET_SET;
          					/* All done and OK. Never bind this one again.	*/
   }
   
   switch (sync) {
      case SOCKET_DELAY:
         if ((flags = fcntl(sp->sd, F_GETFL)) == -1
            || fcntl(sp->sd, F_SETFL, flags & ~O_NDELAY) == -1)
            return -1;
         break;
      case SOCKET_NDELAY:
         if ((flags = fcntl(sp->sd, F_GETFL)) == -1
            || fcntl(sp->sd, F_SETFL, flags | O_NDELAY) == -1)
            return -1;
         break;
      default:
         return -1;
   }
   
   return accept(sp->sd, (struct sockaddr *)&sp->sin, &sp->sinlen);
}

/* sclient tries to connect to a specified server on a given machine.
   The function waits until a connection with the server is established and
   then returns a socket descriptor connected to the server, -1 on error.	*/
int Sclient(SOCKET *sp, char *name, int port) {
   struct hostent *hostent;
   
   if ((hostent = gethostbyname(name)) == 0)
      return -1;
   
   sp->sin.sin_family = (short)hostent->h_addrtype;
   sp->sin.sin_port = htons((unsigned short)port);
   sp->sin.sin_addr.s_addr = *(unsigned long *)hostent->h_addr;
   
   if (connect(sp->sd, (struct sockaddr *)&sp->sin, sp->sinlen) == -1)
      return -1;
   
   return sp->sd;
}

#ifdef HAVE_OPENSSL_SSL_H 
/* modify an open socket to use SSL */
int Sslclient(SOCKET *sp, char *trustedCaDir)
{
   SSL_METHOD *meth;
   SSL_CTX *ctx;
   BIO *sbio;
   X509 *peerCert;
   
   /* assert(sp contains an open socket) */

   SSL_library_init();

   meth = TLSv1_method();
   ctx = SSL_CTX_new(meth);
   if (!ctx)
      return -1;

   sp->ssl = SSL_new(ctx);
   if (sp->ssl == NULL)
      return -1;

   /* don't check return value
    * in case of error, verification will fail later on */
   SSL_CTX_load_verify_locations(ctx, NULL, trustedCaDir);

   sbio = BIO_new_socket(sp->sd, BIO_NOCLOSE);
   if (!sbio) {
      return -1;
   }

   SSL_set_bio(sp->ssl, sbio, sbio); /* read, write bio */

   if (SSL_connect(sp->ssl) != 1) {
      return -1;
   }

   peerCert = SSL_get_peer_certificate(sp->ssl);
   if (!peerCert) {
      return -1;
   }

   X509_free(peerCert);

   if(SSL_get_verify_result(sp->ssl) != X509_V_OK) {
      printf("asmail warning: could not verify server certificate\n");
   }

   return sp->sd;
}
#endif


/* Read a maximum of n-1 characters from sd until a newline or
   a '\0' is received. string stores the input WITHOUT a ter-
   minating newline. Returns number of read characters, without
   terminating \0.
   HINT: pass the SIZE of the array as parameter for n.		 */
   
size_t Sread(int sd, char *string, int n, int timeout)
{
   fd_set rfds;
   struct timeval tv;
   int ret, rcd, i;
   
/* See man 3 select for details ;)

NOTES
       Some  code calls select with all three sets empty, n zero,
       and a non-null timeout as a fairly portable way  to  sleep
       with subsecond precision.

       On  Linux,  timeout  is  modified to reflect the amount of
       time not slept; most other implementations do not do this.
       This  causes  problems  both  when  Linux code which reads
       timeout is ported to other  operating  systems,  and  when
       code  is  ported to Linux that reuses a struct timeval for
       multiple selects in  a  loop  without  reinitializing  it.
       Consider timeout to be undefined after select returns. */

   FD_ZERO(&rfds);
   FD_SET(sd, &rfds);
   i = 0;
   n--;

   do {
      tv.tv_sec = timeout;
      tv.tv_usec = 0;
      
      ret = select(sd+1, &rfds, NULL, NULL, &tv);
      if (ret > 0 && (!(rcd = read(sd, &string[i++], 1)))) {
            string[i] = '\0';
            return 0;
      }
   } while (ret > 0 && rcd > 0 && string[i-1] != 13 && string[i-1] && i < n);
   
   if (ret < 0 || rcd < 0) {
      string[--i] = '\0';
      return -1;
   }

   if (string[i-1] == 13)
      read(sd, &string[--i], 1); /* read 10 from sd */
      
   string[i] = '\0';
   return i;
}

size_t Swrite(int sd, char *string)
{
   return write(sd, string, strlen(string));
}


#ifdef HAVE_OPENSSL_SSL_H 
/* behaves exactly like Sread() above */
size_t Sslread(SOCKET *s, char *string, int n, int timeout)
{
   size_t sizeRead;
   fd_set rfds;
   struct timeval tv;
   int ret;

   if (s->ssl == NULL)
      return -1;

   if (!s->sslBufSize) {
      /* empty buffer: read from SSL */

      FD_ZERO(&rfds);
      FD_SET(s->sd, &rfds);

      tv.tv_sec = timeout;
      tv.tv_usec = 0;

      ret = select((s->sd)+1, &rfds, NULL, NULL, &tv);
      if (ret > 0) {
         s->sslBufMalloc = malloc(BUFSIZ * sizeof(char));
         s->sslBuf = s->sslBufMalloc;
         if (!s->sslBuf)
            return -1;
         s->sslBufSize = SSL_read(s->ssl, s->sslBuf, BUFSIZ);
         if (s->sslBufSize <= 0) {
            free(s->sslBufMalloc);
            return -1;
         }
      }
      else
         return 0;
   }

   /* read from buffer */
   sizeRead = 0;
   while (s->sslBufSize-- && (sizeRead++ < n-1)) {
      if ( ((*string++ = *(s->sslBuf)++) == 0x0d) &&
            (*(s->sslBuf)++ == 0x0a) ) {
         s->sslBufSize--;     /* for the 0x0a we just read */
         string--;   /* undo 0x0d in target string */
         sizeRead--;
         break;
      }
   }

   if (!s->sslBufSize)
      free(s->sslBufMalloc);

   *string = 0x0;
   return sizeRead;
}

size_t Sslwrite(SOCKET *s, char *string)
{
   if (s->ssl == NULL)
      return -1;

   return SSL_write(s->ssl, string, strlen(string));
}
#endif