1 /* distcache, Distributed Session Caching technology
2  * Copyright (C) 2000-2003  Geoff Thorpe, and Cryptographic Appliances, Inc.
3  * Copyright (C) 2004       The Distcache.org project
4  *
5  * This library is free software; you can redistribute it and/or modify it under
6  * the terms of the GNU Lesser General Public License as published by the Free
7  * Software Foundation; using version 2.1 of the License. The copyright holders
8  * may elect to allow the application of later versions of the License to this
9  * software, please contact the author (geoff@distcache.org) if you wish us to
10  * review any later version released by the Free Software Foundation.
11  *
12  * This library is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this library; if not, write to the Free Software Foundation, Inc.,
19  * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21 
22 #define SYS_GENERATING_LIB
23 
24 #include <libsys/pre.h>
25 #include <libnal/nal.h>
26 #include "nal_internal.h"
27 #include <libsys/post.h>
28 
29 static int int_always_one = 1; /* used in setsockopt() */
30 /* Solaris (among possibly many platforms) doesn't know what SOL_TCP is, we need
31  * to use getprotobyname() to find it. The result is stored here. */
32 static int sol_tcp = -1;
33 
34 /*****************************/
35 /* Internal socket functions */
36 /*****************************/
37 
int_sockaddr_size(const nal_sockaddr * addr)38 static int int_sockaddr_size(const nal_sockaddr *addr)
39 {
40 	switch(addr->type) {
41 	case nal_sockaddr_type_ip:
42 		return sizeof(struct sockaddr_in);
43 #ifndef WIN32
44 	case nal_sockaddr_type_unix:
45 		return sizeof(struct sockaddr_un);
46 #endif
47 	default:
48 		break;
49 	}
50 	/* for now at least, should *never* happen */
51 	abort();
52 	/* return 0; */
53 }
54 
int_sock_set_reuse(int fd,const nal_sockaddr * addr)55 static int int_sock_set_reuse(int fd, const nal_sockaddr *addr)
56 {
57 	int reuseVal = 1;
58 	if(addr->type != nal_sockaddr_type_ip)
59 		return 1;
60 	if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
61 			(char *)(&reuseVal), sizeof(reuseVal)) != 0)
62 		return 0;
63 	return 1;
64 }
65 
int_sock_bind(int fd,const nal_sockaddr * addr)66 static int int_sock_bind(int fd, const nal_sockaddr *addr)
67 {
68 	socklen_t addr_size = int_sockaddr_size(addr);
69 	nal_sockaddr tmp;
70 
71 	if(addr->type == nal_sockaddr_type_unix)
72 		/* Stevens' book says do it, so I do. Unfortunately - this
73 		 * actually needs additional file-locking to prevent one
74 		 * application stealing another's listener (without him
75 		 * noticing it's gone even!). */
76 		unlink(addr->val.val_un.sun_path);
77 	SYS_memcpy(nal_sockaddr, &tmp, addr);
78 	if(bind(fd, (struct sockaddr *)&tmp, addr_size) != 0)
79 		return 0;
80 	return 1;
81 }
82 
83 /**********************/
84 /* nal_sock functions */
85 /**********************/
86 
nal_sock_set_nagle(int fd,int use_nagle,nal_sockaddr_type type)87 int nal_sock_set_nagle(int fd, int use_nagle, nal_sockaddr_type type)
88 {
89 #ifndef WIN32
90 	if(use_nagle || (type != nal_sockaddr_type_ip))
91 		return 1;
92 
93 	if(sol_tcp == -1) {
94 		struct protoent *p = getprotobyname("tcp");
95 		if(!p) {
96 #if SYS_DEBUG_LEVEL > 1
97 			SYS_fprintf(SYS_stderr, "Error, couldn't obtain SOL_TCP\n");
98 #endif
99 			return 0;
100 		}
101 		sol_tcp = p->p_proto;
102 	}
103 
104 	if(setsockopt(fd, sol_tcp, TCP_NODELAY, &int_always_one,
105 			sizeof(int_always_one)) != 0) {
106 #if SYS_DEBUG_LEVEL > 1
107 		SYS_fprintf(SYS_stderr, "Error, couldn't disable Nagle algorithm\n");
108 #endif
109 		return 0;
110 	}
111 #endif
112 	return 1;
113 }
114 
nal_sock_sockaddr_from_ipv4(nal_sockaddr * addr,const char * start_ptr)115 int nal_sock_sockaddr_from_ipv4(nal_sockaddr *addr, const char *start_ptr)
116 {
117 	char *tmp_ptr;
118 	char *fini_ptr;
119 	struct hostent *ip_lookup;
120 	/* struct sockaddr_in in_addr; */
121 	unsigned long in_ip_piece;
122 	unsigned char in_ip[4];
123 	int no_ip = 0;
124 
125 	addr->caps = 0;
126 	/* We're an IPv4 address, and start_ptr points to the first character
127 	 * of the address part */
128 	if(strlen(start_ptr) < 1) return 0;
129 	/* Logic: if our string contains another ":" we assume it's of the form
130 	 * nnn.nnn.nnn.nnn:nnn, otherwise assume IP[v4]:nnn. Exception,
131 	 * if it's of the form IP[v4]::nnn, we treat it as equivalent to one
132 	 * colon. */
133 	if(((fini_ptr = strstr(start_ptr, ":")) == NULL) ||
134 			(start_ptr == fini_ptr)) {
135 		/* No colon, skip the IP address - this is listen-only */
136 		no_ip = 1;
137 		/* If it's a double colon, we need to increment start_ptr */
138 		if(fini_ptr)
139 			start_ptr++;
140 		goto ipv4_port;
141 	}
142 	/* Create a temporary string for the isolated hostname/ip-address */
143 	tmp_ptr = SYS_malloc(char, (int)(fini_ptr - start_ptr) + 1);
144 	if(!tmp_ptr)
145 		return 0;
146 	SYS_memcpy_n(char, tmp_ptr, start_ptr,
147 		(int)(fini_ptr - start_ptr));
148 	tmp_ptr[(int)(fini_ptr - start_ptr)] = '\0';
149 	ip_lookup = gethostbyname(tmp_ptr);
150 	SYS_free(char, tmp_ptr);
151 	if(!ip_lookup)
152 		/* Host not understood or recognised */
153 		return 0;
154 	/* Grab the IP address and move on (h_addr_list[0] is signed char?!) */
155 	SYS_memcpy_n(char, (char *)in_ip, ip_lookup->h_addr_list[0], 4);
156 	/* Align start_ptr to the start of the "port" number. */
157 	start_ptr = fini_ptr + 1;
158 	/* Ok, this is an address that could be used for connecting */
159 	addr->caps |= NAL_ADDRESS_CAN_CONNECT;
160 
161 ipv4_port:
162 	if(strlen(start_ptr) < 1)
163 		return 0;
164 	/* start_ptr points to the first character of the port part */
165 	in_ip_piece = strtoul(start_ptr, &fini_ptr, 10);
166 	if((in_ip_piece > 65535) || (*fini_ptr != '\0'))
167 		return 0;
168 	/* populate the sockaddr_in structure */
169 	addr->val.val_in.sin_family = AF_INET;
170 	if(no_ip)
171 		addr->val.val_in.sin_addr.s_addr = INADDR_ANY;
172 	else
173 		SYS_memcpy_n(unsigned char,
174 			(unsigned char *)&addr->val.val_in.sin_addr.s_addr, in_ip, 4);
175 	addr->val.val_in.sin_port = htons((unsigned short)in_ip_piece);
176 	/* ipv4 addresses are always good for listening */
177 	addr->caps |= NAL_ADDRESS_CAN_LISTEN;
178 	addr->type = nal_sockaddr_type_ip;
179 	return 1;
180 }
181 
182 #ifndef WIN32
nal_sock_sockaddr_from_unix(nal_sockaddr * addr,const char * start_ptr)183 int nal_sock_sockaddr_from_unix(nal_sockaddr *addr, const char *start_ptr)
184 {
185 	struct sockaddr_un un_addr;
186 
187 	un_addr.sun_family = AF_UNIX;
188 	SYS_strncpy(un_addr.sun_path, start_ptr, UNIX_PATH_MAX);
189 	/* Now sandblast the sockaddr_un structure onto the sockaddr structure
190 	 * (which one hopes is greater than or equal to it in size :-). */
191 	SYS_zero(nal_sockaddr, addr);
192 	SYS_memcpy(struct sockaddr_un, &addr->val.val_un, &un_addr);
193 	addr->type = nal_sockaddr_type_unix;
194 	addr->caps = NAL_ADDRESS_CAN_LISTEN | NAL_ADDRESS_CAN_CONNECT;
195 	return 1;
196 }
197 #endif
198 
nal_sock_create_socket(int * fd,const nal_sockaddr * addr)199 int nal_sock_create_socket(int *fd, const nal_sockaddr *addr)
200 {
201 	int tmp_fd = -1;
202 	switch(addr->type) {
203 	case nal_sockaddr_type_ip:
204 		tmp_fd = socket(PF_INET, SOCK_STREAM, 0);
205 		break;
206 #ifndef WIN32
207 	case nal_sockaddr_type_unix:
208 		tmp_fd = socket(PF_UNIX, SOCK_STREAM, 0);
209 		break;
210 #endif
211 	default:
212 		/* Should never happen */
213 		abort();
214 	}
215 	if(tmp_fd < 0) {
216 #if SYS_DEBUG_LEVEL > 1
217 		SYS_fprintf(SYS_stderr, "Error, can't create socket\n\n");
218 #endif
219 		return 0;
220 	}
221 	*fd = tmp_fd;
222 	return 1;
223 }
224 
225 #ifndef WIN32
nal_sock_create_unix_pair(int sv[2])226 int nal_sock_create_unix_pair(int sv[2])
227 {
228 	if(socketpair(PF_UNIX, SOCK_STREAM, 0, sv) != 0) {
229 #if SYS_DEBUG_LEVEL > 1
230 		SYS_fprintf(SYS_stderr, "Error, can't create socketpair\n\n");
231 #endif
232 		return 0;
233 	}
234 	return 1;
235 }
236 #endif
237 
nal_sock_connect(int fd,const nal_sockaddr * addr,int * established)238 int nal_sock_connect(int fd, const nal_sockaddr *addr,
239 			int *established)
240 {
241 	socklen_t addr_size = int_sockaddr_size(addr);
242 	nal_sockaddr tmp;
243 
244 	SYS_memcpy(nal_sockaddr, &tmp, addr);
245 	if(connect(fd, (struct sockaddr *)&tmp, addr_size) != 0) {
246 #ifdef WIN32
247 		if(WSAGetLastError() != WSAEWOULDBLOCK)
248 #else
249 		if(errno != EINPROGRESS)
250 #endif
251 		{
252 #if SYS_DEBUG_LEVEL > 1
253 			SYS_fprintf(SYS_stderr, "Error, couldn't connect\n\n");
254 #endif
255 			return 0;
256 		}
257 		/* non-blocking connect ... connect() succeeded, but it may yet
258 		 * fail without a single byte going anywhere. */
259 		*established = 0;
260 	} else
261 		*established = 1;
262 	return 1;
263 }
264 
nal_sock_listen(int fd,const nal_sockaddr * addr)265 int nal_sock_listen(int fd, const nal_sockaddr *addr)
266 {
267 	if(!int_sock_set_reuse(fd, addr) || !int_sock_bind(fd, addr))
268 		return 0;
269 	if(listen(fd, NAL_LISTENER_BACKLOG) != 0)
270 		return 0;
271 	return 1;
272 }
273 
nal_sock_accept(int listen_fd,int * conn)274 int nal_sock_accept(int listen_fd, int *conn)
275 {
276 	if((*conn = accept(listen_fd, NULL, NULL)) == -1) {
277 #if SYS_DEBUG_LEVEL > 1
278 		SYS_fprintf(SYS_stderr, "Error, accept failed\n\n");
279 #endif
280 		return 0;
281 	}
282 	return 1;
283 }
284 
nal_sock_is_connected(int fd)285 int nal_sock_is_connected(int fd)
286 {
287 	int t;
288 	socklen_t t_len = sizeof(t);
289 	/* the ugly cast is necessary with my system headers to avoid warnings,
290 	 * but there's probably a reason and/or autoconf things to do with
291 	 * this. */
292 	if((getsockopt(fd, SOL_SOCKET, SO_ERROR, &t,
293 			(unsigned int *)&t_len) != 0) || (t != 0))
294 		return 0;
295 	return 1;
296 }
297 
298 /* To save code-duplication, work out requirements once */
299 #if !defined(WIN32) && defined(HAVE_GETSOCKNAME) && defined(HAVE_STRTOUL) && \
300 	defined(HAVE_CHOWN) && defined(HAVE_CHMOD) && defined(HAVE_GETPWNAM)
301 #define NAL_SOCKADDR_UNIX
302 #endif
303 
nal_sockaddr_get(nal_sockaddr * addr,int fd)304 int nal_sockaddr_get(nal_sockaddr *addr, int fd)
305 {
306 #ifdef NAL_SOCKADDR_UNIX
307 	socklen_t pathlen = sizeof(addr->val.val_un);
308 	/* I get a "pointer target in arg 3 differ in signedness" warning from
309 	 * gcc despite the fact I am following the exact prototype! So the
310 	 * (void*) cast is just to avoid false positives from -Werror. */
311 	if(getsockname(fd, (struct sockaddr *)&addr->val.val_un,
312 				(void*)&pathlen) != 0)
313 		return 0;
314 	addr->type = nal_sockaddr_type_unix;
315 	addr->caps = 0; /* Can't listen or connect */
316 	return 1;
317 #else
318 	return 0;
319 #endif
320 }
321 
nal_sockaddr_chown(const nal_sockaddr * addr,const char * username,const char * groupname)322 int nal_sockaddr_chown(const nal_sockaddr *addr, const char *username,
323 			const char *groupname)
324 {
325 #ifdef NAL_SOCKADDR_UNIX
326 	/* according to chown(2), -1 can be used as an owner or group value to
327 	 * specify "no change". */
328 	struct passwd *p = (username ? getpwnam(username) : NULL);
329 	uid_t uid = (p ? p->pw_uid : (uid_t)-1);
330 	gid_t gid = (p ? p->pw_gid : (uid_t)-1);
331 #if defined(HAVE_GETGRNAM)
332 	struct group *g = (groupname ? getgrnam(groupname) : NULL);
333 	/* Err if 'groupname' is invalid */
334 	if(groupname && !g) return 0;
335 	if(g) gid = g->gr_gid;
336 #endif
337 	/* Err if 'username' is invalid */
338 	if(username && !p) return 0;
339 	/* Err if 'addr' is not unix */
340 	if(addr->type != nal_sockaddr_type_unix) return 0;
341 	if(chown(addr->val.val_un.sun_path, uid, gid) != 0) return 0;
342 	return 1;
343 #else
344 	return 0;
345 #endif
346 }
nal_sockaddr_chmod(const nal_sockaddr * addr,const char * octal_string)347 int nal_sockaddr_chmod(const nal_sockaddr *addr, const char *octal_string)
348 {
349 #ifdef NAL_SOCKADDR_UNIX
350 	unsigned long n;
351 	char *endptr;
352 	/* Err if 'addr' is not unix */
353 	if(addr->type != nal_sockaddr_type_unix) return 0;
354 	/* Parse the octal */
355 	n = strtol(octal_string, &endptr, 8);
356 	if ((endptr == octal_string) || (*endptr != '\0') || (n == ULONG_MAX))
357 		return 0;
358 	if (chmod(addr->val.val_un.sun_path, n) != 0) return 0;
359 	return 1;
360 #else
361 	return 0;
362 #endif
363 }
364