?? socket.c
字號:
/* 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.12.2.6 1999/08/19 10:26:07 joe 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>#ifdef HAVE_SYS_SELECT_H#include <sys/select.h>#endif#include <netinet/in.h>#include <arpa/inet.h>#include <netdb.h>#include <errno.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 = recv(sock, bp, len, MSG_PEEK)) <= 0) { DEBUG( DEBUG_SOCKET, "recv: %d - error: %s\n", n, strerror(errno) ); return(-1); } if ((newline = memchr(bp, '\n', n)) != NULL) n = newline - bp + 1; if ((n = sock_read(sock, bp, n)) == -1) { DEBUG( DEBUG_SOCKET, "read error: %s\n", strerror(errno) ); 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. */ DEBUG( DEBUG_SOCKET, "Using Linux 2.2 sendfile...\n" ); 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; } DEBUG( DEBUG_SOCKET, "sendfile failed... falling back.\n" );#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, (readlen==-1)?BUFSIZ:(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; } if( readlen != -1 ) curlen -= rdlen; /* 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_binary( 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 ); }}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -