/* * Copyright (c) 1993 W. Richard Stevens. All rights reserved. * Permission to use or modify this software and its documentation only for * educational purposes and without fee is hereby granted, provided that * the above copyright notice appear in all copies. The author makes no * representations about the suitability of this software for any purpose. * It is provided "as is" without express or implied warranty. */ #include "sock.h" int servopen(char *host, char *port) { int fd, newfd, i, on, pid; const char *protocol; struct in_addr inaddr; struct servent *sp; protocol = udp ? "udp" : "tcp"; /* Initialize the socket address structure */ bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; /* Caller normally wildcards the local Internet address, meaning a connection will be accepted on any connected interface. We only allow an IP address for the "host", not a name. */ if (host == NULL) servaddr.sin_addr.s_addr = htonl(INADDR_ANY); /* wildcard */ else { if (inet_aton(host, &inaddr) == 0) err_quit("invalid host name for server: %s", host); servaddr.sin_addr = inaddr; } /* See if "port" is a service name or number */ if ( (i = atoi(port)) == 0) { if ( (sp = getservbyname(port, protocol)) == NULL) err_ret("getservbyname() error for: %s/%s", port, protocol); servaddr.sin_port = sp->s_port; } else servaddr.sin_port = htons(i); if ( (fd = socket(AF_INET, udp ? SOCK_DGRAM : SOCK_STREAM, 0)) < 0) err_sys("socket() error"); if (reuseaddr) { on = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) err_sys("setsockopt of SO_REUSEADDR error"); } #ifdef SO_REUSEPORT if (reuseport) { on = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)) < 0) err_sys("setsockopt of SO_REUSEPORT error"); } #endif /* Bind our well-known port so the client can connect to us. */ if (bind(fd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0) err_sys("can't bind local address"); join_mcast(fd, &servaddr); if (udp) { buffers(fd); if (foreignip[0] != 0) { /* connect to foreignip/port# */ bzero(&cliaddr, sizeof(cliaddr)); if (inet_aton(foreignip, &cliaddr.sin_addr) == 0) err_quit("invalid IP address: %s", foreignip); cliaddr.sin_family = AF_INET; cliaddr.sin_port = htons(foreignport); /* connect() for datagram socket doesn't appear to allow wildcarding of either IP address or port number */ if (connect(fd, (struct sockaddr *) &cliaddr, sizeof(cliaddr)) < 0) err_sys("connect() error"); } sockopts(fd, 1); return(fd); /* nothing else to do */ } buffers(fd); /* may set receive buffer size; must do here to get correct window advertised on SYN */ sockopts(fd, 0); /* only set some socket options for fd */ listen(fd, listenq); if (pauselisten) sleep_us(pauselisten*1000); /* lets connection queue build up */ if (dofork) TELL_WAIT(); /* initialize synchronization primitives */ for ( ; ; ) { i = sizeof(cliaddr); if ( (newfd = accept(fd, (struct sockaddr *) &cliaddr, &i)) < 0) err_sys("accept() error"); if (dofork) { if ( (pid = fork()) < 0) err_sys("fork error"); if (pid > 0) { close(newfd); /* parent closes connected socket */ WAIT_CHILD(); /* wait for child to output to terminal */ continue; /* and back to for(;;) for another accept() */ } else { close(fd); /* child closes listening socket */ } } /* child (or iterative server) continues here */ if (verbose) { /* Call getsockname() to find local address bound to socket: local internet address is now determined (if multihomed). */ i = sizeof(servaddr); if (getsockname(newfd, (struct sockaddr *) &servaddr, &i) < 0) err_sys("getsockname() error"); /* Can't do one fprintf() since inet_ntoa() stores the result in a static location. */ fprintf(stderr, "connection on %s.%d ", INET_NTOA(servaddr.sin_addr), ntohs(servaddr.sin_port)); fprintf(stderr, "from %s.%d\n", INET_NTOA(cliaddr.sin_addr), ntohs(cliaddr.sin_port)); } buffers(newfd); /* setsockopt() again, in case it didn't propagate from listening socket to connected socket */ sockopts(newfd, 1); /* can set all socket options for this socket */ if (dofork) TELL_PARENT(getppid()); /* tell parent we're done with terminal */ return(newfd); } }