File: socket.c

package info (click to toggle)
screem 0.2.1-1
  • links: PTS
  • area: main
  • in suites: potato
  • size: 3,748 kB
  • ctags: 2,270
  • sloc: ansic: 26,366; sh: 7,453; makefile: 512; sed: 93; perl: 18
file content (444 lines) | stat: -rw-r--r-- 12,831 bytes parent folder | download
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
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
/* 
   socket handling routines
   Copyright (C) 1998, Joe Orton <joe@orton.demon.co.uk>, except where
   otherwise indicated.
                                                                     
   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; either version 2 of the License, or
   (at your option) any later version.
  
   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.

   $Id: socket.c,v 1.1.1.1 1999/11/21 19:47:27 davek Exp $
*/

#include <config.h>

#include <sys/types.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#include <sys/socket.h>
#include <sys/stat.h>

#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

#ifdef HAVE_LX22_SENDFILE

#include <linux/unistd.h>

/* We have sendfile()... the macro does not produce a prototype
 * though, so we prototype it too.  */

_syscall4(int, sendfile, int, out_fd, int, in_fd, off_t *, offset,
	  size_t, count);

int sendfile(int out_fd, int in_fd, off_t *offset, size_t count);

#endif /* HAVE_LX22_SENDFILE */

#include <fcntl.h>
#include <stdio.h>
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif 
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif /* HAVE_STDLIB_H */
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif /* HAVE_UNISTD_H */

#include "socket.h"
#include "common.h"
#include "frontend.h"

/* sock_read is read() with a timeout of SOCKET_TIMEOUT. */
int sock_read( const int sock, void *buffer, const size_t count ) {
    int ret;
    ret = sock_block( sock, SOCKET_TIMEOUT );
    if( ret > 0 ) /* Got data */
	ret = read( sock, buffer, count );
    return ret;
}

/* sock_recv is recv() with a timeout of SOCKET_TIMEOUT */
int sock_recv( const int sock, void *buffer, const size_t count,
	       const unsigned int flags ) {
    int ret;
    ret = sock_block( sock, SOCKET_TIMEOUT );
    if( ret > 0 ) /* Got data */
	ret = recv( sock, buffer, count, flags );
    return ret;
}

/* Blocks waiting for input on the given socket for the given time. */
int sock_block( const int sock, const int timeout ) {
    static struct timeval tv;
    static fd_set fds;

    /* Init the fd set */
    FD_ZERO( &fds );
    FD_SET( sock, &fds );
    /* Set the timeout */
    tv.tv_sec = timeout;
    tv.tv_usec = 0;
    return select( sock+1, &fds, NULL, NULL, &tv );
}

/* Send the given line down the socket with CRLF appended. 
 * Returns 0 on success or -1 on failure. */
int send_line( const int sock, const char *line ) {
    char *buffer;
    int ret;
    buffer = malloc( strlen(line) + 3 );
    strcpy( buffer, line );
    /* Add \r\n on the end - Unix servers will ignore the \r, 
     * Windows ones require \r\n */
    strcat( buffer, "\r\n" );
    ret = send_string( sock, buffer );
    free( buffer );
    return ret;
}

/* Send a block of data down the given fd.
 * Returns 0 on success or -1 on failure */
int send_data( const int fd, const char *data, const size_t length ) {
    size_t sent, wrote;
    const char *pnt;

    sent = 0;
    pnt = data;
    while( sent < length ) {
	wrote = write( fd, pnt, length-sent );
	if( wrote < 0 ) {
	    perror( "write" );
	    return -1;
	}
	sent += wrote;
    }
    return 0;
}

/* Sends the given string down the given socket.
 * Returns 0 on success or -1 on failure. */
int send_string( const int sock, const char *data ) {
    return send_data( sock, data, strlen( data ) );
}

/* This is from from Eric Raymond's fetchmail (SockRead() in socket.c)
 * since I wouldn't have a clue how to do it properly.
 * This function is Copyright (C) Eric Raymond.
 *
 *  Actually, it's now been slightly modified to return -2
 *  if we don't find a newline within len. This is necessary 
 *  for reading HTTP header lines, which have no maximum length.
 */
int read_line( const int sock, char *buffer, int len) {
    char *newline, *bp = buffer;
    int n;
    
    if (--len < 1)
	return(-1);
    do {
	/*
	 * The reason for these gymnastics is that we want two things:
	 * (1) to read \n-terminated lines,
	 * (2) to return the true length of data read, even if the
	 *     data coming in has embedded NULS.
	 */
	if ((n = sock_recv(sock, bp, len, MSG_PEEK)) <= 0)
	    return(-1);
	if ((newline = memchr(bp, '\n', n)) != NULL)
	    n = newline - bp + 1;
	if ((n = sock_read(sock, bp, n)) == -1)
	    return(-1);
	bp += n;
	len -= n;
	if( len == 0 ) return -2;
    } while (!newline && len);
    *bp = '\0';
    return bp - buffer;
}

/* Reads readlen bytes from srcfd and writes to destfd.
 * (Not all in one go, obviously).
 * If readlen == -1, then it reads from srcfd until EOF.
 * Returns number of bytes written to destfd, or -1 on error.
 * Calls fe_transfer_progress( a, b ) during transfers, where
 *  a = bytes transferred so far, and b = readlen
 */
size_t transfer( const int srcfd, const int destfd, const size_t readlen ) {
    char buffer[BUFSIZ], *pnt;
    int rdlen, wrlen, len2;

    size_t curlen; /* total bytes yet to read from srcfd */
    size_t sumwrlen; /* total bytes written to destfd */

    if( readlen == -1 ) {
	curlen = BUFSIZ; /* so the buffer size test works */
    } else {
#ifdef HAVE_LX22_SENDFILE
	/* We can only use sendfile if we know how much we're sending.
	 * Eyeballing kernel source shows offset can be NULL. */
	wrlen = sendfile( destfd, srcfd, NULL, readlen );
	/* I think that sendfile only works on some filesystems...
	 * on others it will give -EINVAL */
	if( wrlen >= 0 || errno != EINVAL ) {
	    return wrlen;
	}
#endif /* HAVE_LX22_SENDFILE */
	curlen = readlen; /* everything to do */
    }
    sumwrlen = 0; /* nowt done yet */

    while( curlen > 0 ) {
	/* Get a chunk... if the number of bytes that are left to read
	 * is less than the buffer size, only read that many bytes. */
	rdlen = sock_read( srcfd, buffer, min( BUFSIZ, curlen ) );
	fe_transfer_progress( sumwrlen, readlen );
	if( rdlen < 0 ) { 
	    perror("read");
	    return -1;
	} else if( rdlen == 0 ) { 
	    /* End of file... get out of here */
	    break;
	}
	/* Otherwise, we have bytes!  Write them to destfd... might
	 * only manage to write a few of them at a time, so we have
	 * to deal with that too. */
	/* Replace this with a call to send_data? */
	pnt = buffer;
	len2 = rdlen;
	while( len2 > 0 ) {
	    wrlen = write( destfd, pnt, len2 );
	    if( wrlen < 0 ) { 
		perror( "write" );
		return -1;
	    }
	    len2 -= wrlen;
	    pnt += wrlen;
	    sumwrlen += wrlen;
	}
    }
    return sumwrlen;
}

/* Reads buflen bytes into buffer until it's full.
 * Returns 0 on success, -1 on error */
int read_data( const int sock, char *buffer, int buflen ) {
    char *pnt; /* current position within buffer */
    int len;
    pnt = buffer;
    while( buflen > 0 ) {
	len = sock_read( sock, pnt, buflen );
	if( len < 0 ) return len;
	buflen -= len;
	pnt += len;
    }
    return 0;
}

/* Dump the given filename down the given socket.
 * Returns non-zero value if successful */
int send_file( const int sock, const char *filename ) {
    int fd, wrote;
    struct stat fs;

#if defined (__EMX__) || defined(__CYGWIN__)
    if( (fd = open( filename, O_RDONLY | O_BINARY )) < 0 ) {
#else
    if( (fd = open( filename, O_RDONLY )) < 0 ) {
#endif     
	perror( "open" ); 
	return -1;
    }
    if( fstat( fd, &fs ) < 0 ) {
	perror( "fstat" );
	close( fd );
	return -2;
    }
    /* What's the Right Thing to do? Choices:
     *  a) Let transfer send everything from fd until EOF
     *    + If the EOF pos changes, we'll know and can signal an error
     *    - Unsafe - the transfer might not end if someone does 
     *        yes > file
     *  b) Tell transfer to send only the number of bytes from the stat()
     *    + Safe - the transfer WILL end.
     *    - If the EOF pos changes, we'll have a partial (corrupt) file.
     * I'm not sure. I think (a) gets my vote but it doesn't allow
     * nice transfer progress bars in the FE under the current API
     * so we go with (b).
     */
    wrote = transfer( fd, sock, fs.st_size );
    close( fd ); /* any point in checking that one? */
    /* Return whether we transferred the correct number of bytes */
    return (wrote == fs.st_size );
}

/* Dump the given filename down the given socket, in ASCII translation
 * mode. Returns non-zero value if successful. */
int send_file_ascii( const int sock, const char *filename ) {
    int ret;
    char buffer[BUFSIZ], *pnt;
    FILE *f;
    
    f = fopen( filename, "r" );
    if( f == NULL ) {
	perror( "fopen" ); 
	return -1;
    }

    /* Init to success */
    ret = 1;

    while(1) {
	if( fgets( buffer, BUFSIZ - 1, f ) == NULL ) {
	    if( ferror( f ) ) {
		ret = 0;
		break;
	    }
	    /* Finished upload */
	    ret = 1;
	    break;
	}
	/* To send in ASCII mode, we need to send CRLF as the EOL.
	 * We might or might not already have CRLF-delimited lines.
	 * So we mess about a bit to ensure that we do.
	 */
	pnt = strchr( buffer, '\r' );
	if( pnt == NULL ) {
	    /* We need to add the CR in */
	    pnt = strchr( buffer, '\n' );
	    if( pnt == NULL ) {
		/* No CRLF found at all */
		pnt = strchr( buffer, '\0' );
		if( pnt == NULL ) /* crud in buffer */
		    pnt = buffer;
	    }
	    /* Now, pnt points to the first character after the 
	     * end of the line, i.e., where we want to put the CR.
	     */
	    *pnt++ = '\r';
	    /* And lob in an LF afterwards */
	    *pnt-- = '\n';
	}
	/* At this point, pnt points to the CR.
	 * We send everything between pnt and the beginning of the buffer,
	 * +2 for the CRLF
	 */
	if( send_data( sock, buffer, (pnt - buffer) +2 ) != 0 ) {
	    ret = 0;
	    break;
	}
    }
    fclose( f ); /* any point in checking that one? */
    /* Return true */
    return ret;
}

/* Dump from given socket into given file. Reads only filesize bytes,
 * or until EOF if filesize == -1.
 * Returns number of bytes written on success, or -1 on error */
int recv_file( const int sock, const char *filename, const size_t filesize ) {
    int fd;
    size_t wrote;
#if defined (__EMX__) || defined(__CYGWIN__)
    /* We have to set O_BINARY, thus need open(). Otherwise it should be
       equivalent to creat(). */
    if( (fd = open( filename, O_WRONLY|O_TRUNC|O_CREAT|O_BINARY, 0644 )) < 0 ) {
	perror( "open" );
	return -1;
    }
#else
    if( (fd = creat( filename, 0644 )) < 0 ) {
	perror( "creat" );
	return -1;
    }
#endif
    wrote = transfer( sock, fd, filesize );
    if( close( fd ) == -1 ) {
	/* Close failed - file was not written correctly */
	return -1;
    }
    if( filesize == -1 ) {
	return wrote;
    } else {
	return (wrote==filesize);
    }
}

/* Do a name lookup on given hostname, writes the address into
 * given address buffer. Return -1 on failure.
 */
int host_lookup( const char *hostname, struct in_addr *addr ) {
    struct hostent *hp;
    unsigned long laddr;
    
    DEBUG( DEBUG_SOCKET, "host_lookup: trying inet_addr\n" );
    laddr = (unsigned long)inet_addr(hostname);
    if ((int)laddr == -1) {
	/* inet_addr failed. */
	DEBUG( DEBUG_SOCKET, "trying gethostbyname\n" );
	hp = gethostbyname(hostname);
	if( hp == NULL ) {
	    DEBUG( DEBUG_SOCKET, "gethostbyname failed\n" );
	    return -1;
	}
	DEBUG( DEBUG_SOCKET, "gethostbyname worked.\n" );
	memcpy( addr, hp->h_addr, hp->h_length );
    } else {
	DEBUG( DEBUG_SOCKET, "inet_addr succeeded\n" );
	addr->s_addr = laddr;
    }
    return 0;
}

/* Opens a socket to the given port at the given address.
 * Returns -1 on failure, or the socket on success. 
 * portnum must be in HOST byte order */
int socket_connect( const struct in_addr addr, const int portnum ) {
    struct sockaddr_in sa;
    int sock;
    /* Look up the host name */
    /* Create the socket */
    if( ( sock = socket(AF_INET, SOCK_STREAM, 0) ) < 0)
	return -1;
    /* Connect the socket */
    sa.sin_family = AF_INET;
    sa.sin_port = htons(portnum); /* host -> net byte orders */
    sa.sin_addr = addr;
    if( connect(sock, (struct sockaddr *) &sa, sizeof(struct sockaddr_in)) < 0 )
	return -1;
    /* Success - return the socket */
    return sock;
}

/* Closes given socket */
void socket_close( const int sock ) {
    close( sock );
}

/* Returns HOST byte order port of given name */
int get_tcp_port( const char *name ) {
    struct servent *ent;
    ent = getservbyname( name, "tcp" );
    if( ent == NULL ) {
	return 0;
    } else {
	return ntohs( ent->s_port );
    }
}