/* * TFTP network handling for TCP/IP connection. */ #include "netdefs.h" #include #include #include #include extern int errno; #ifndef CLIENT #ifndef SERVER either CLIENT or SERVER must be defined #endif #endif int sockfd = -1; /* fd for socket of server */ char openhost[MAXHOSTNAMELEN] = { 0 }; /* remember host's name */ extern int traceflag; /* TFTP variable */ extern struct sockaddr_in tcp_srv_addr; /* set by tcp_open() */ extern struct servent tcp_serv_info; /* set by tcp_open() */ #ifdef CLIENT /* * Open the network connection. Client version. */ int net_open(host, service, port) char *host; /* name of other system to communicate with */ char *service; /* name of service being requested */ int port; /* if > 0, use as port#, else use value for service */ { if ( (sockfd = tcp_open(host, service, port)) < 0) return(-1); DEBUG2("net_open: host %s, port# %d", inet_ntoa(tcp_srv_addr.sin_addr), ntohs(tcp_srv_addr.sin_port)); strcpy(openhost, host); /* save the host's name */ return(0); } #endif /* CLIENT */ /* * Close the network connection. Used by client and server. */ net_close() { DEBUG2("net_close: host = %s, fd = %d", openhost, sockfd); close(sockfd); sockfd = -1; } /* * Send a record to the other end. Used by client and server. * With a stream socket we have to preface each record with its length, * since TFTP doesn't have a record length as part of each record. * We encode the length as a 2-byte integer in network byte order. */ net_send(buff, len) char *buff; int len; { register int rc; short templen; DEBUG1("net_send: sent %d bytes", len); templen = htons(len); rc = writen(sockfd, (char *) &templen, sizeof(short)); if (rc != sizeof(short)) err_dump("writen error of length prefix"); rc = writen(sockfd, buff, len); if (rc != len) err_dump("writen error"); } /* * Receive a record from the other end. Used by client and server. */ int /* return #bytes in packet, or -1 on EINTR */ net_recv(buff, maxlen) char *buff; int maxlen; { register int nbytes; short templen; /* value-result parameter */ again1: if ( (nbytes = readn(sockfd, (char *) &templen, sizeof(short))) < 0) { if (errno == EINTR) { errno = 0; /* assume SIGCLD */ goto again1; } err_dump("readn error for length prefix"); } if (nbytes != sizeof(short)) err_dump("error in readn of length prefix"); templen = ntohs(templen); /* #bytes that follow */ if (templen > maxlen) err_dump("record length too large"); again2: if ( (nbytes = readn(sockfd, buff, templen)) < 0) { if (errno == EINTR) { errno = 0; /* assume SIGCLD */ goto again2; } err_dump("readn error"); } if (nbytes != templen) err_dump("error in readn"); DEBUG1("net_recv: got %d bytes", nbytes); return(nbytes); /* return the actual length of the message */ } #ifdef SERVER struct sockaddr_in tcp_cli_addr; /* set by accept() */ /* * Initialize the network connection for the server, when it has *not* * been invoked by inetd. */ net_init(service, port) char *service; /* the name of the service we provide */ int port; /* if nonzero, this is the port to listen on; overrides the standard port for the service */ { struct servent *sp; /* * We weren't started by a master daemon. * We have to create a socket ourselves and bind our well-known * address to it. */ bzero((char *) &tcp_srv_addr, sizeof(tcp_srv_addr)); tcp_srv_addr.sin_family = AF_INET; tcp_srv_addr.sin_addr.s_addr = htonl(INADDR_ANY); if (service != NULL) { if ( (sp = getservbyname(service, "tcp")) == NULL) err_dump("net_init: unknown service: %s/tcp", service); tcp_serv_info = *sp; /* structure copy */ if (port > 0) tcp_srv_addr.sin_port = htons(port); /* caller's value */ else tcp_srv_addr.sin_port = sp->s_port; /* service's value */ } else { if (port <= 0) { err_ret("tcp_open: must specify either service or port"); return(-1); } tcp_srv_addr.sin_port = htons(port); } /* * Create the socket and Bind our local address so that any * client can send to us. */ if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) err_dump("net_init: can't create stream socket"); if (bind(sockfd, (struct sockaddr *) &tcp_srv_addr, sizeof(tcp_srv_addr)) < 0) err_dump("net_init: can't bind local address"); /* * And set the listen parameter, telling the system that we're * ready to accept incoming connection requests. */ listen(sockfd, 5); } /* * Initiate the server's end. * We are passed a flag that says whether or not we were started * by a "master daemon," such as the inetd program under 4.3BSD. * A master daemon will have already waited for a message to arrive * for us, and will have already set up the connection to the client. * If we weren't started by a master daemon, then we must wait for a * client's request to arrive. */ int net_open(inetdflag) int inetdflag; /* true if inetd started us */ { register int newsockfd, childpid, nbytes; int clilen, on; on = 1; if (inetdflag) { #ifdef BSD /* assumes 4.3BSD inetd */ /* * When we're fired up by inetd under 4.3BSD, file * descriptors 0, 1 and 2 are sockets to the client. */ sockfd = 0; /* descriptor for net_recv() to read from */ return(0); /* done */ #endif /* BSD inetd specifics */ } /* * For the concurrent server that's not initiated by inetd, * we have to wait for a connection request to arrive, * then fork a child to handle the client's request. * Beware that the accept() can be interrupted, such as by * a previously spawned child process that has terminated * (for which we caught the SIGCLD signal). */ again: clilen = sizeof(tcp_cli_addr); newsockfd = accept(sockfd, (struct sockaddr *) &tcp_cli_addr, &clilen); if (newsockfd < 0) { if (errno == EINTR) { errno = 0; goto again; /* probably a SIGCLD that was caught */ } err_dump("accept error"); } /* * Fork a child process to handle the client's request. * The parent returns the child pid to the caller, which is * probably a concurrent server that'll call us again, to wait * for the next client request to this well-known port. */ if ( (childpid = fork()) < 0) err_dump("server can't fork"); else if (childpid > 0) { /* parent */ close(newsockfd); /* close new connection */ return(childpid); /* and return */ } /* * Child process continues here. * First close the original socket so that the parent * can accept any further requests that arrive there. * Then set "sockfd" in our process to be the descriptor that * we are going to process. */ close(sockfd); sockfd = newsockfd; return(0); /* return to process the connection */ } #endif /* SERVER */