/* ===================================================== * INETSOCK.C (v1.4) * ======================== * * C routines for INTERNET SOCKET programming * (previously known as "tcpsock.c") * * - initiallement crees pour package Simula: SOCKET.SIM * - Version 1.5 (mars 2000) * Correction de certains headers. * - Version 1.4 (nov 1997: J. Vaucher) * Fonction utilitaires ajoutees * - Version 1.2 (aout 1994: J. Vaucher) * - Version 1.0 (juin 1992) * S. Some & J. Vaucher ******************************************************** STREAMS - int internet_connect( host, port) ==> fd: Creates CLIENT socket connected to ; returns "fd" - int internet_server(port) ==> fd Creates STREAM SERVER socket; returns "fd" - int internet_accept (fd, &client_adr) Receives connect request. Returns "fd" of socket for private interaction, As well as Client Address - int stream_write (fd, &Buffer, Len) - int stream_read (fd, &Buffer, Len) - char read_char (fd) - int read_string (fd, &Buffer, Len, Delimiter_C) DATAGRAMS - int datagram_socket () => Creates Datagram socket and returns "fd" - int send_datagram ( fd, &Buffer, Len, &SockAdr) - int receive_datagram( fd, &Buffer, Len, &SockAdr) UTILITY - int sockadr_size (): - int chars_avail (fd): Returns the number of characters which can be read (if ZERO, a read operation on "fd" will BLOCK) ADDRESSING - host_aton ("www.jsp.umontrea.ca") ==> Host (integer) - char *host_ntoa( Host ) ==> "www.jsp.umontrea.ca" - char *host_ntod( Host ) ==> "132.102.54.12" - make_sockadr( &SockAdr, Host, Port) - split_sockadr(&SockAdr, Host, Port) - socketport (int fd) -> Port - serviceport ("http","tcp") -> Port ============================================================= */ /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx NETWORK ADDRESSING FUNCTIONS xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx These functions allow conversions between the various address formats used with INTERNET sockets. For example: Host addresses: --------------- Internally these are 32 bit unsigned integers, but externally, 2 text formats are be used: - Host address (alphabetic): "www.iro.umontreal.ca" - Host address in Dotted form: "132.102.54.12" Port addresses: --------------- Internally, these are 16 bit integers. Most often port numbers for user sockets are allocated 'randomly' by the operating system. On the other hand, established operating system services such as FTP, MAIL or HTTP (Web servers) use ports with standard numbers which are the same on all machines connected to the internet. Standard port numbers all have values below 1024 and are listed in the file "/etc/services". Socket addresses: ----------------- A socket address is a data structure which combines a HOST address and a PORT address with a "tag" field which indicates the TYPE of the socket (UNIX or INTERNET). Socket addresses are required to set up a server's address (BIND) or to CONNECT to a stream server. They are also used in operations which return the address of an incoming communication (ACCEPT, RECVFROM). Note that nothing is a socket address indicates wether it IS used for STREAM or DATAGRAM communication - that aspect is fixed by the SOCKET function which allocates the required data structures. Unfortunately for programmers, since socket addresses can have different sizes and formats depending of their type, programs have to deal with various data structure definitions. These 3 are typical: - struct sockaddr_in : internet socket - struct sockaddr_un : unix sockets - struct sockaddr : generic socket type used in the prototype definitions of socket functions Remember that functions which deal with sockets all require POINTERs to socket address - actual parameters of the form "&sa" rather than "sa". Our functions, require pointers to 'internet' sockets (type: struct sockaddr_in * sockAdr ). Internally, these are 'cast' into the type required by the UNIX procedures. Socket numbers: --------------- After a socket is created, it is referenced through a socket number which is the same as a file descriptor (fd) for other UNIX IO (tty, file, dev, pipe, etc...). These numbers are small integer values used by UNIX functions as indexes into the local file table. Socket NUMBERS are thus quite different from Socket ADDRESSES. ROUTINES PROVIDED: ================== a) Host addresses ----------------- Conversion between the internal 32 bit version and the human-readable alphabetic formats. - host_aton ( String ) -> integer The STRING can be IN either of the 2 standard formats: "www.iro.umontreal.ca" or "132.102.54.12". It can also be NULL or a zero length string; in which case, the address of the machine on which the program is running will be returned. - host_ntoa ( Integer ) -> * char This returns a pointer to the STRING address in standard ("www.iro.umontreal.ca") format - host_ntod ( Integer ) -> * char This returns a pointer to the address in dotted ("132.102.54.12") format b) Port addresses ----------------- - Port_aton( Service, Protocol) --> integer This gives the Port number for well-known services like "http" or "smtp" c) Socket address <==> ------------------------------------ - make_socketAdr ( ^sockaddr_in, Host, Port) - split_socketAdr ( ^sockaddr_in, ^Host, ^Port) "make_socketAdr" initialises a socket address from Host and Port integer values. "split_socketAdr" goes the other way; given an initialized socket address, it extracts and returns the Host and Port integer values. [Note: the character '^' is used to indicate that the parameter should be passed by reference so that either i) a value can be returned or ii) a data structure, used as input, doesn't have to be copied d) Socket number ==> -------------------------------- - socket_to_HostPort(fd, ^Host, ^Port) Given a socket number (fd) this provides the Host and Port numbers as integer values. aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa */ #include #include #include #include #include #include #include #include #include #include #include //#include /* FORWARD definitions */ void make_sockadr( struct sockaddr_in *,u_long,int); int32_t host_aton (const char *name); /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ /* STREAMS */ /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ /* --------------------------------------------------------------- */ int internet_connect( char *host, int port ) /* --------------------------------------------------------------- Used by CLIENTs to set up STREAM connection to known server RETURNS: "fd" Example: connecting to JSP,http fd = internet_connect("www.jsp.umontreal.ca", 80); read(fd,....); close(fd); --------------------------------------------------------------- */ { int res, fd; struct sockaddr_in sa; fd = socket (AF_INET, SOCK_STREAM, 0) ; if (fd < 0) { perror("internet_connect: socket"); exit(1); } printf("%s:%d\n", host, port ); make_sockadr( &sa, host_aton(host), port) ; res = connect(fd, (struct sockaddr *) &sa, sizeof(sa)); if (res < 0) { close(fd); perror("internet_connect: connect"); exit(1); } return(fd); } /* -------------------------------------------------------------- */ int internet_server (int Port) /* -------------------------------------------------------------- Used by SERVER to set up STREAM socket Port. A PORT number=0 means that the number will be assigned by the system and can be obtained from the returned "fd" with "sock2port" RETURNS: "fd" Example: connecting to JSP,http fd = internet_server(0); printf("Server receving on port: %d \n", socketport(fd)); fd2 = inet_accept(fd,.....); --------------------------------------------------------------*/ { struct sockaddr_in sa; int fd; fd = socket (AF_INET, SOCK_STREAM, 0) ; if (fd < 0) { perror("internet_server: socket"); exit(1); } make_sockadr( &sa, INADDR_ANY, Port) ; if ( bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) { close(fd); perror("internet_server: connect"); exit(1); } listen(fd, 5); /* max # of queued connects */ return(fd); } /* -------------------------------------------------------- */ int internet_accept( int fd, struct sockaddr_in * cli_adr) /* ----------------------------------------------------------------- Used by SERVER to accept connection request. RETURNS: 1) new "fd" for private link to client 2) the client address is returned in 2nd parameter Example: fd2 = inet_accept(fd, &cli_adr ); split_sockadr( &cli_adr, C_host, & C_port); printf("Request from Host: %s, Port: %d\n", C_host,C_port); write( fd2, Msg, MsgLen); close(fd2); ---------------------------------------------------------------- */ { int newfd; int dummy ; newfd = accept( fd, (struct sockaddr *) cli_adr, & dummy); if (newfd < 0) { perror("internet_accept"); exit(1); } return(newfd); } /* ================ Input/Output to Streams ===================== */ /* ---------------------------------------------------- * Write "n" bytes to a stream socket * ---------------------------------------------------- */ int stream_write(int fd, char *ptr, int n) { int res; while (n > 0) { if ((res = write(fd, ptr, n)) <= 0) return (res); n -= res; ptr += res; } return (0); } /* ---------------------------------------------------- * Read "n" bytes from a stream socket * ---------------------------------------------------- */ int stream_read(int fd, char * ptr, int nbytes) { int n, res; n = nbytes; while (n > 0) { if ((res = read(fd, ptr, n)) < 0) return (res); if (res == 0) break; n -= res; ptr += res; } return (nbytes - n); } /* ------------------ * Read a character * ------------------ */ int read_char(int fd) { char c; if ( read( fd, &c, 1) == 1) return(c) ; else return( -1 ); } /* -------------------------------------------- * Read a STRING delimited by character C * * Can be used to read a LINE with C = '\n' * -------------------------------------------- */ int read_string(int fd, char *ptr, int maxlen, char C) { int n, nread; char c; for (n=1; n < maxlen; n++) { if ( (nread = read(fd, &c, 1)) == 1) { *ptr++ = c; if (c == C) break; } else if (nread == 0 && n>0) break; else return (nread); } *ptr = '\0'; return(n) ; } /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ /* DATAGRAMS */ /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ /* ---------------------------------------------------------- */ int datagram_socket () /* ========================================================== Creates a Datagram Internet Socket and returns its "fd". The PORT number is assigned by the system and can be obtained from the returned "fd" with "sock2port" RETURNS: "fd" Example: fd = udp_socket(); printf("Datagram socket created on PORT: %d \n", sock2port(fd)); ------------------------------------------------------------ */ { int fd ; struct sockaddr_in sock_adr ; fd = socket (AF_INET, SOCK_DGRAM, 0) ; if (fd < 0) { perror("datagram_socket: socket"); exit(1); } make_sockadr( &sock_adr, INADDR_ANY, 0); if ( bind( fd, (struct sockaddr *) &sock_adr, sizeof(sock_adr)) < 0) { close( fd ); perror("datagram_socket: bind"); exit(1); } return (fd) ; } /* -------------------------------------------------------- */ int send_datagram( int fd, char *buff, int n, struct sockaddr_in *DestAdr) /* -------------------------------------------------------- */ /* envoie buff a sock pour hostn au port port*/ { return (sendto (fd, buff, n, 0, (struct sockaddr *) DestAdr, sizeof(*DestAdr)) ); } /* -------------------------------------------------------- */ int receive_datagram( int fd, char *buff, int n, struct sockaddr_in *PeerAdr) /* -------------------------------------------------------- */ { int res, Len ; res = recvfrom (fd, buff, n-1, 0, (struct sockaddr *) PeerAdr, &Len) ; if (res >= 0 ) buff[res] = '\0' ; ; return(res); } /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ /* UTILITY */ /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ /* -------------------------------------------- * Returns the number of bytes that can be * read without blocking. * -------------------------------------------- */ /* int chars_avail (int fd) { int n; ioctl(fd, FIONREAD, &n); return(n); } */ /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ /* NETWORK ADDRESSING FUNCTIONS */ /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ #define PATHSIZE 100 static char hostName[PATHSIZE]; char * host_name() { gethostname (hostName, PATHSIZE); return ( hostName ); } /* ---------------------------------------------------------- */ int32_t host_aton (const char *name) /* ========================================================== Takes HOST names in either the numeric 132.104.25.16 or the usual format, "www.iro.umontreal.ca" and returns a 32 bit address. ---------------------------------------------------------- */ { struct hostent *hostStruct; struct in_addr *hostNode; if ( name == NULL || strcmp (name, "") == 0) gethostname (hostName, PATHSIZE); // else if (isdigit (name[0])) // return (inet_addr (name)); else strcpy (hostName, name); hostStruct = gethostbyname ( hostName); if (hostStruct == NULL) return (unsigned long)(NULL); hostNode = (struct in_addr *) hostStruct->h_addr; return (hostNode->s_addr); } /* ------------------------------------------------------------ */ char *host_ntoa( u_long Host ) /* ============================================================ Produces the "nil.iro.umontreal.ca" form of an INET address. ------------------------------------------------------------ */ { return( gethostbyaddr( (char *) &Host, sizeof(u_long), AF_INET) -> h_name ); } /* -------------------------------------------------------- */ char *host_ntod( u_long Host ) /* ========================================================== Produces the "132.45.44.01" form of an INET address. ------------------------------------------------------------ */ { struct in_addr in_a; in_a.s_addr = Host; return ( inet_ntoa(in_a) ); } /* ---------------------------------------------- */ void make_sockadr( struct sockaddr_in *SockAdr, u_long Host, int Port) /* ========================================================== Initializes a Socket Address from integer Host and Port addresses ------------------------------------------------------------ */ { memset( SockAdr, 0, sizeof(*SockAdr) ); /* Probably unecessary */ SockAdr->sin_family = AF_INET ; SockAdr->sin_addr.s_addr = Host; SockAdr->sin_port = htons(Port) ; } /* -------------------------------------------------------- */ void split_sockadr( struct sockaddr_in *SockAdr, u_long *Host, int *Port) /* ========================================================== Inverse of "make_inetadr": extracts the Host and Port numbers from a Socket Address. Can be used to get information about CLIENTS for both STREAM and DATAGRAM connections. Example: fd2 = internet_accept(fd, & cli_adr ); split_sockadr( cli_adr, &C_host, &C_port); printf("Request from Host: %s, Port: %d\n", Host_ntoa(C_host), C_port); write( fd2, Msg, MsgLen); ------------------------------------------------------------ */ { *Port = ntohs(SockAdr->sin_port) ; *Host = ntohl(SockAdr->sin_addr.s_addr ); } /* -------------------------------------------------------- */ int socketport (int fd) /* -------------------------------------------------------- */ /* retourne le numero de port auquel est lie le socket "fd" */ { struct sockaddr_in SockAdr; int n; n = sizeof(SockAdr); if (getsockname (fd, (struct sockaddr *) &SockAdr, &n) < 0) { perror("socket_to_HostPort"); exit(1); } return (ntohs(SockAdr.sin_port)) ; } /* -------------------------------------------------------- */ int serviceport (char *service, char *protocol ) /* ========================================================== Looks at /etc/srvices to find PORT number for a given service.i.e. service2port("http","tcp") returns 80. Note: protocol = { "tcp", "udp" } ------------------------------------------------------------ */ { struct servent *serv; if ( protocol == NULL || strcmp (protocol, "") == 0) protocol = "tcp"; serv = getservbyname( service, protocol); if ( serv == NULL ) { fprintf(stderr,"servicePort: %s service not found %s.\n", protocol, service); fflush(stderr); exit(1); }; return (serv->s_port) ; } /* ------------------------------------------------------------ */ int sockadr_size () /* ============================================================ Allows Simula programs to create buffers (TEXT) of correct size for ADDRESSES as required by functions like INET_ACCEPT. ------------------------------------------------------------ */ { return (sizeof( struct sockaddr_in)) ; }