00001 /* 00002 Simple Socket Interface 00003 AUP2, Sec. 8.04.5 00004 00005 Copyright 2003 by Marc J. Rochkind. All rights reserved. 00006 May be copied only for purposes and under conditions described 00007 on the Web page www.basepath.com/aup/copyright.htm. 00008 00009 The Example Files are provided "as is," without any warranty; 00010 without even the implied warranty of merchantability or fitness 00011 for a particular purpose. The author and his publisher are not 00012 responsible for any damages, direct or incidental, resulting 00013 from the use or non-use of these Example Files. 00014 00015 The Example Files may contain defects, and some contain deliberate 00016 coding mistakes that were included for educational reasons. 00017 You are responsible for determining if and how the Example Files 00018 are to be used. 00019 00020 */ 00021 #ifdef DARWIN 00022 /* Following needed to get socklen_t defined. */ 00023 #define _BSD_SOCKLEN_T_ int 00024 #endif /* DARWIN */ 00025 #include "defs.h" 00026 #include "ssi.h" 00027 #ifdef SOLARIS 00028 #define __EXTENSIONS__ 00029 #endif 00030 #include <netdb.h> 00031 #undef __EXTENSIONS__ 00032 #include <sys/un.h> 00033 #include <netinet/in.h> 00034 #include <arpa/inet.h> 00035 /*[set_fd_hwm]*/ 00036 static void set_fd_hwm(SSI *ssip, int fd) 00037 { 00038 if (fd > ssip->ssi_fd_hwm) 00039 ssip->ssi_fd_hwm = fd; 00040 } 00041 00042 static void reset_fd_hwm(SSI *ssip, int fd) 00043 { 00044 if (fd == ssip->ssi_fd_hwm) 00045 ssip->ssi_fd_hwm--; 00046 } 00047 /*[make_sockaddr]*/ 00048 bool make_sockaddr(struct sockaddr *sa, socklen_t *len, const char *name, 00049 int domain, bool will_bind) 00050 { 00051 struct addrinfo *infop = NULL; 00052 00053 if (domain == AF_UNIX) { 00054 struct sockaddr_un *sunp = (struct sockaddr_un *)sa; 00055 00056 if (strlen(name) >= sizeof(sunp->sun_path)) { 00057 errno = ENAMETOOLONG; 00058 EC_FAIL 00059 } 00060 strcpy(sunp->sun_path, name); 00061 sunp->sun_family = AF_UNIX; 00062 *len = sizeof(*sunp); 00063 } 00064 else { 00065 struct addrinfo hint; 00066 char nodename[SSI_NAME_SIZE], *servicename; 00067 00068 memset(&hint, 0, sizeof(hint)); 00069 hint.ai_family = domain; 00070 hint.ai_socktype = SOCK_STREAM; 00071 if (will_bind) 00072 hint.ai_flags = AI_PASSIVE; 00073 strcpy(nodename, name); 00074 servicename = strchr(nodename, ':'); 00075 if (servicename == NULL) { 00076 errno = EINVAL; 00077 EC_FAIL 00078 } 00079 *servicename = '\0'; 00080 servicename++; 00081 ec_ai( getaddrinfo(nodename, servicename, &hint, &infop) ) 00082 memcpy(sa, infop->ai_addr, infop->ai_addrlen); 00083 *len = infop->ai_addrlen; 00084 freeaddrinfo(infop); 00085 } 00086 return true; 00087 00088 EC_CLEANUP_BGN 00089 if (infop != NULL) 00090 freeaddrinfo(infop); 00091 return false; 00092 EC_CLEANUP_END 00093 } 00094 /*[]*/ 00095 /* 00096 Name of the form "//host:port" uses AF_INET to access that host and port. Otherwise, AF_UNIX is used, and the name has no slashes, colon, or port. (E.g., "someservice".) Because numeric port numbers are used, there need be no entry in /etc/services. host can be a name with an entry in /etc/hosts (or via DNS), or it can be a numeric IP address (e.g., 192.168,0,1). 00097 */ 00098 /*[ssi_open]*/ 00099 SSI *ssi_open(const char *name_server, bool server) 00100 { 00101 SSI *ssip = NULL; 00102 struct sockaddr_storage sa; 00103 socklen_t sa_len; 00104 00105 ec_null( ssip = (SSI *)calloc(1, sizeof(SSI)) ) 00106 ssip->ssi_server = server; 00107 if (strncmp(name_server, "//", 2) == 0) { 00108 ssip->ssi_domain = AF_INET; 00109 name_server += 2; 00110 } 00111 else { 00112 ssip->ssi_domain = AF_UNIX; 00113 if (ssip->ssi_server) 00114 (void)unlink(name_server); 00115 } 00116 if (strlen(name_server) >= sizeof(ssip->ssi_name_server)) { 00117 errno = ENAMETOOLONG; 00118 EC_FAIL 00119 } 00120 strcpy(ssip->ssi_name_server, name_server); 00121 ec_false( make_sockaddr((struct sockaddr *)&sa, &sa_len, 00122 ssip->ssi_name_server, ssip->ssi_domain, ssip->ssi_server) ) 00123 ec_neg1( ssip->ssi_fd = socket(ssip->ssi_domain, SOCK_STREAM, 0) ) 00124 set_fd_hwm(ssip, ssip->ssi_fd); 00125 if (ssip->ssi_domain == AF_INET) { 00126 /* 00127 SO_REUSEADDR supposedly makes the connection less reliable, 00128 but it's too inconvenient to do development without it set. 00129 */ 00130 int socket_option_value = 1; 00131 00132 ec_neg1( setsockopt(ssip->ssi_fd, SOL_SOCKET, SO_REUSEADDR, 00133 &socket_option_value, sizeof(socket_option_value)) ) 00134 } 00135 if (ssip->ssi_server) { 00136 FD_ZERO(&ssip->ssi_fd_set); 00137 ec_neg1( bind(ssip->ssi_fd, (struct sockaddr *)&sa, sa_len) ) 00138 ec_neg1( listen(ssip->ssi_fd, SOMAXCONN) ) 00139 FD_SET(ssip->ssi_fd, &ssip->ssi_fd_set); 00140 } 00141 else 00142 ec_neg1( connect(ssip->ssi_fd, (struct sockaddr *)&sa, sa_len) ) 00143 return ssip; 00144 00145 EC_CLEANUP_BGN 00146 free(ssip); 00147 return NULL; 00148 EC_CLEANUP_END 00149 } 00150 /*[ssi_close]*/ 00151 bool ssi_close(SSI *ssip) 00152 { 00153 if (ssip->ssi_server) { 00154 int fd; 00155 00156 for (fd = 0; fd <= ssip->ssi_fd_hwm; fd++) 00157 if (FD_ISSET(fd, &ssip->ssi_fd_set)) 00158 (void)close(fd); 00159 if (ssip->ssi_domain == AF_UNIX) 00160 (void)unlink(ssip->ssi_name_server); 00161 } 00162 else 00163 (void)close(ssip->ssi_fd); 00164 free(ssip); 00165 return true; 00166 } 00167 /*[ssi_close_fd]*/ 00168 bool ssi_close_fd(SSI *ssip, int fd) 00169 { 00170 ec_neg1( close(fd) ); 00171 FD_CLR(fd, &ssip->ssi_fd_set); 00172 reset_fd_hwm(ssip, fd); 00173 return true; 00174 00175 EC_CLEANUP_BGN 00176 return false; 00177 EC_CLEANUP_END 00178 } 00179 /*[ssi_wait_server]*/ 00180 int ssi_wait_server(SSI *ssip) 00181 { 00182 if (ssip->ssi_server) { 00183 fd_set fd_set_read; 00184 int fd, clientfd; 00185 struct sockaddr_un from; 00186 socklen_t from_len = sizeof(from); 00187 00188 while (true) { 00189 fd_set_read = ssip->ssi_fd_set; 00190 ec_neg1( select(ssip->ssi_fd_hwm + 1, &fd_set_read, NULL, NULL, 00191 NULL) ) 00192 for (fd = 0; fd <= ssip->ssi_fd_hwm; fd++) 00193 if (FD_ISSET(fd, &fd_set_read)) { 00194 if (fd == ssip->ssi_fd) { 00195 ec_neg1( clientfd = accept(ssip->ssi_fd, 00196 (struct sockaddr *)&from, &from_len) ); 00197 FD_SET(clientfd, &ssip->ssi_fd_set); 00198 set_fd_hwm(ssip, clientfd); 00199 continue; 00200 } 00201 else 00202 return fd; 00203 } 00204 } 00205 } 00206 else { 00207 errno = ENOTSUP; 00208 EC_FAIL 00209 } 00210 00211 EC_CLEANUP_BGN 00212 return -1; 00213 EC_CLEANUP_END 00214 } 00215 /*[ssi_get_server_fd]*/ 00216 int ssi_get_server_fd(SSI *ssip) 00217 { 00218 return ssip->ssi_fd; 00219 } 00220 /*[]*/ 00221 #if 0 00222 bool ssi_send(SSI *ssip, void *buf, size_t bufsize) 00223 { 00224 if (ssip->ssi_server) { 00225 ec_neg1( sendto(ssip->ssi_fd_client, buf, bufsize, 0, 00226 (struct sockaddr *)&ssip->ssi_sa, ssip->ssi_sa_len) ) 00227 } 00228 else 00229 ec_neg1( send(ssip->ssi_fd, buf, bufsize, 0) ) 00230 return true; 00231 00232 EC_CLEANUP_BGN 00233 return false; 00234 EC_CLEANUP_END 00235 } 00236 00237 bool ssi_receive(SSI *ssip, void *buf, size_t bufsize) 00238 { 00239 if (ssip->ssi_server) { 00240 ec_neg1( ssip->ssi_fd_client = ssi_wait_server(ssip) ) 00241 ssip->ssi_sa_len = sizeof(ssip->ssi_sa); 00242 // check on short count... go back for more? 00243 ec_neg1( recvfrom(ssip->ssi_fd_client, buf, bufsize, 00244 0, (struct sockaddr *)&ssip->ssi_sa, &ssip->ssi_sa_len) ) 00245 } 00246 else 00247 ec_neg1( recv(ssip->ssi_fd, buf, bufsize, 0) ) 00248 return true; 00249 00250 EC_CLEANUP_BGN 00251 return false; 00252 EC_CLEANUP_END 00253 } 00254 #endif