1 /*
2  * util.c -- common utility routines for thrulay.
3  *
4  * Written by Stanislav Shalunov, http://www.internet2.edu/~shalunov/
5  *            Bernhard Lutzmann, belu@users.sf.net
6  *            Federico Montesino Pouzols, fedemp@altern.org
7  *
8  * Copyright 2003, 2006, Internet2.
9  * Legal conditions are in file LICENSE
10  * (MD5 = ecfa50d1b0bfbb81b658c810d0476a52).
11  */
12 
13 #ifdef HAVE_CONFIG_H
14 #include <config.h>
15 #endif
16 
17 #include <stdio.h>
18 #include <string.h>
19 #include <stdlib.h>
20 #include <sys/types.h>
21 #ifndef WIN32
22 #include <sys/socket.h>
23 #include <netinet/in.h>
24 #endif
25 #include "util.h"
26 #include "rcs.h"
27 
28 #ifndef SOL_IP
29 #ifdef IPPROTO_IP
30 #define SOL_IP		IPPROTO_IP
31 #endif
32 #endif
33 
34 #ifndef SOL_IPV6
35 #ifdef IPPROTO_IPV6
36 #define SOL_IPV6	IPPROTO_IPV6
37 #endif
38 #endif
39 
40 RCS_ID("@(#) $Id: util.c,v 1.1.1.1.2.6 2006/08/20 18:06:19 fedemp Exp $")
41 
42 #ifndef HAVE_GETRUSAGE
43 #ifdef WIN32
44 /* Implementation of getrusage based on Windows native calls. Note
45    this is a minimal implementation that only fills in the fields used
46    by the thrulay server (ru_utime and ru_stime) with the times for
47    the current thread. */
getrusage(int process,struct rusage * rusage)48 int getrusage(int process, struct rusage* rusage)
49 {
50 	FILETIME f_kernel, f_user;
51 	HANDLE thread = OpenThread(THREAD_QUERY_INFORMATION, FALSE,
52 				   GetCurrentThreadId());
53 
54 	if (GetThreadTimes(thread, NULL, NULL, &f_kernel, &f_user) ) {
55 		LONGLONG t_user = *(LONGLONG*)&f_user;
56 		LONGLONG t_kernel = *(LONGLONG*)&f_kernel;
57 		if (rusage) {
58 			rusage->ru_utime.tv_usec = (DWORD)(t_user/10);
59 			rusage->ru_stime.tv_usec = (DWORD)(t_kernel / 10);
60 		}
61 	} else {
62 		if (rusage)
63 			memset(rusage, 0, sizeof(rusage));
64 	}
65 
66 	return 0;
67 }
68 #endif
69 #endif
70 
71 #ifdef WIN32
72 
73 int
gettimeofday(struct timeval * tv,void * tz)74 gettimeofday(struct timeval *tv,  void *tz)
75 {
76 	DWORD ms = GetTickCount();
77 	tv->tv_sec = ms / 1000;
78 	tv->tv_usec = ms * 1000;
79 	return 0;
80 }
81 
82 #ifdef HAVE_W32_GETADDRINFO
inet_ntop(int af,const void * src,char * dst,socklen_t cnt)83 const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt)
84 {
85 	socklen_t len;
86 
87 	switch (af) {
88 	case AF_INET:
89 		len = sizeof(struct sockaddr_in);
90 		break;
91 	case AF_INET6:
92 		len = sizeof(struct sockaddr_in6);
93 		break;
94 	default:
95 		return NULL;
96 	}
97 
98 	if ( 0 != getnameinfo((struct sockaddr *)src, len, dst, cnt,
99 			      NULL, 0, NI_NUMERICHOST) )
100 		return NULL;
101 
102 	return dst;
103 }
104 
gai_strerror(int errcode)105 const char *gai_strerror(int errcode)
106 {
107 	static char result[256];
108 
109 	/* Obviously, this is not the best solution. A list of error
110 	   descriptions should be added at some time. */
111 	snprintf(result,sizeof(result),
112 		 "error code: %d; no description available on Windows",
113 		 errcode);
114 
115 	return result;
116 }
117 
118 #endif
119 
120 #endif /* ifdef WIN32 */
121 
122 void
error(int errcode,const char * msg)123 error(int errcode, const char *msg)
124 {
125 	const char *prefix;
126 	int fatal = 1;
127 
128 	if (errcode == ERR_FATAL) {
129 		prefix = "fatal";
130 	} else if (errcode == ERR_WARNING) {
131 		prefix = "warning";
132 		fatal = 0;
133 	} else {
134 		prefix = "UNKNOWN ERROR TYPE";
135 	}
136 	fprintf(stderr, "%s: %s\n", prefix, msg);
137 	if (fatal)
138 		exit(1);
139 }
140 
141 ssize_t
recv_exactly(int d,void * buf,size_t nbytes)142 recv_exactly(int d, void *buf, size_t nbytes)
143 {
144 	ssize_t rc = 0;
145 	size_t bytes_read = 0;
146 	while (bytes_read < nbytes &&
147 	/* In Win32, read/write don't work with sockets! */
148 #ifndef WIN32
149 	       (rc = read(d, (char *)buf+bytes_read,
150 			  nbytes-bytes_read)) > 0)
151 #else
152 	       (rc = recv(d, (char *)buf+bytes_read,
153 			  nbytes-bytes_read, 0)) > 0)
154 #endif
155 		bytes_read += rc;
156 	return rc == -1? rc: (ssize_t) bytes_read;
157 }
158 
159 ssize_t
write_exactly(int d,const void * buf,size_t nbytes)160 write_exactly(int d, const void *buf, size_t nbytes)
161 {
162 	ssize_t rc = 0;
163 	size_t bytes_written = 0;
164 	while (bytes_written < nbytes &&
165 	       (rc = write(d, (const char *)buf+bytes_written,
166 			   nbytes-bytes_written)) > 0)
167 		bytes_written += rc;
168 	return rc == -1? rc: (ssize_t) bytes_written;
169 }
170 
171 ssize_t
send_exactly(int d,const void * buf,size_t nbytes)172 send_exactly(int d, const void *buf, size_t nbytes)
173 {
174 	ssize_t rc = 0;
175 	size_t bytes_written = 0;
176 	/* In Win32, read/write don't work with sockets! */
177 #ifndef WIN32
178 	while (bytes_written < nbytes &&
179 	       (rc = write(d, (const char *)buf+bytes_written,
180 			   nbytes-bytes_written)) > 0)
181 #else
182 	while (bytes_written < nbytes &&
183 	       (rc = send(d, (const char *)buf+bytes_written,
184 			  nbytes-bytes_written, 0)) > 0)
185 #endif
186 		bytes_written += rc;
187 	return rc == -1? rc: (ssize_t) bytes_written;
188 }
189 
190 double
time_diff(const struct timeval * tv1,const struct timeval * tv2)191 time_diff(const struct timeval *tv1, const struct timeval *tv2)
192 {
193 	return (double) (tv2->tv_sec - tv1->tv_sec)
194 		+ (double) (tv2->tv_usec - tv1->tv_usec)/1000000.0;
195 }
196 
197 /* Set window size for file descriptor FD (which must point to a
198    socket) to WINDOW for given direction DIRECTION (typically
199    SO_SNDBUF or SO_RCVBUF).  Try hard.  Return actual window size. */
200 int
set_window_size_directed(int fd,int window,int direction)201 set_window_size_directed(int fd, int window, int direction)
202 {
203 	int rc, try, w;
204 	unsigned int optlen = sizeof w;
205 
206 	rc = getsockopt(fd, SOL_SOCKET, direction, (char *)&w, &optlen);
207 	if (rc == -1)
208 		return -1;
209 	if (window <= 0)
210 		return w;
211 
212 	try = window;
213 	do {
214 		rc = setsockopt(fd, SOL_SOCKET, direction,
215 				(char *)&try, optlen);
216 		try *= 7;
217 		try /= 8;
218 	} while (try > w && rc == -1);
219 
220 	rc = getsockopt(fd, SOL_SOCKET, direction, (char *)&w, &optlen);
221 	if (rc == -1)
222 		return -1;
223 	else
224 		return w;
225 }
226 
227 /* Set window size for file descriptor FD (which must point to a
228    socket) to WINDOW.  Try hard.  Return actual window size. */
229 int
set_window_size(int fd,int window)230 set_window_size(int fd, int window)
231 {
232 	int send, receive;
233 
234 	send = set_window_size_directed(fd, window, SO_SNDBUF);
235 	receive = set_window_size_directed(fd, window, SO_RCVBUF);
236 	return send < receive? send: receive;
237 }
238 
239 /* Set RFC 2474 style DSCP value for TOS byte. Returns 0 on success, -1 on
240  * failure. */
241 int
set_dscp(int sock,uint8_t dscp)242 set_dscp(int sock, uint8_t dscp)
243 {
244 	int optname = IP_TOS;
245 	int optlevel = SOL_IP;
246 	int sopt;
247 
248 	if ((dscp & ~0x3F)) {
249 		fprintf(stderr, "Error: set_dscp(): bad DSCP value.\n");
250 		return -1;
251 	}
252 
253 	/* Shift to set CU to zero
254 	 *
255 	 *   0   1   2   3   4   5   6   7
256 	 * +---+---+---+---+---+---+---+---+
257 	 * |         DSCP          |  CU   |
258 	 * +---+---+---+---+---+---+---+---+
259 	 */
260 	sopt = dscp << 2;
261 
262 	{
263 		struct sockaddr_storage addr;
264 		socklen_t len = sizeof(addr);
265 
266 		if (getsockname(sock, (struct sockaddr *)&addr,
267 				&len) == -1) {
268 			perror("getsockname");
269 			return -1;
270 		}
271 
272 		switch (((struct sockaddr *)&addr)->sa_family) {
273 		case AF_INET:
274 			optlevel = SOL_IP;
275 			optname = IP_TOS;
276 			break;
277 		case AF_INET6:
278 #ifdef IPV6_TCLASS
279 			optlevel = SOL_IPV6;
280 			optname = IPV6_TCLASS;
281 			break;
282 #else
283 			error(ERR_WARNING, "system does not support setting "
284 					"DSCP value in IPv6 traffic class.");
285 			return 0;	/* return 0 so we don't get two
286 					   error messages. */
287 #endif
288 		default:
289 			error(ERR_WARNING, "set_dscp(): Unknown address "
290 					"family");
291 			return -1;
292 		}
293 	}
294 
295 	if (setsockopt(sock, optlevel, optname, (char *)&sopt,
296 		       sizeof(sopt)) == -1) {
297 		perror("setsockopt");
298 		return -1;
299 	}
300 
301 	return 0;
302 }
303 
304 #define NTP_EPOCH_OFFSET	2208988800ULL
305 
306 /*
307  * Convert `timeval' structure value into NTP format (RFC 1305) timestamp.
308  * The ntp pointer must resolve to already allocated memory (8 bytes) that
309  * will contain the result of the conversion.
310  * NTP format is 4 octets of unsigned integer number of whole seconds since
311  * NTP epoch, followed by 4 octets of unsigned integer number of
312  * fractional seconds (both numbers are in network byte order).
313  */
314 void
tv2ntp(const struct timeval * tv,char * ntp)315 tv2ntp(const struct timeval *tv, char *ntp)
316 {
317 	uint32_t msb, lsb;
318 
319 	msb = tv->tv_sec + NTP_EPOCH_OFFSET;
320 	lsb = (uint32_t)((double)tv->tv_usec * 4294967296.0 / 1000000.0);
321 
322 	msb = htonl(msb);
323 	lsb = htonl(lsb);
324 
325 	memcpy(ntp, &msb, sizeof(msb));
326 	memcpy(ntp + sizeof(msb), &lsb, sizeof(lsb));
327 }
328 
329 /*
330  * Convert 8-byte NTP format timestamp into `timeval' structure value.
331  * The counterpart to tv2ntp().
332  */
333 void
ntp2tv(struct timeval * tv,const char * ntp)334 ntp2tv(struct timeval *tv, const char *ntp)
335 {
336 	uint32_t msb, lsb;
337 
338 	memcpy(&msb, ntp, sizeof(msb));
339 	memcpy(&lsb, ntp + sizeof(msb), sizeof(lsb));
340 
341 	msb = ntohl(msb);
342 	lsb = ntohl(lsb);
343 
344 	tv->tv_sec = msb - NTP_EPOCH_OFFSET;
345 	tv->tv_usec = (uint32_t)((double)lsb * 1000000.0 / 4294967296.0);
346 }
347 
348 /* Make sure 0 <= tv.tv_usec < 1000000.  Return 0 if it was normal,
349  * positive number otherwise. */
350 int
normalize_tv(struct timeval * tv)351 normalize_tv(struct timeval *tv)
352 {
353 	int result = 0;
354 
355 	while (tv->tv_usec >= 1000000) {
356 		tv->tv_usec -= 1000000;
357 		tv->tv_sec++;
358 		result++;
359 	}
360 	while (tv->tv_usec < 0) {
361 		tv->tv_usec += 1000000;
362 		tv->tv_sec--;
363 		result++;
364 	}
365 	return result;
366 }
367 
368 #ifndef HAVE_SYSLOG_H
369 
370 /* Definition of logging functions when missing. Logs are written to a
371    '.log' file. */
372 
373 FILE *syslog_file;
374 int syslog_pid;
375 char *syslog_filename_suffix = "-syslog.log";
376 char *syslog_ident;
377 
378 void
openlog(const char * ident,int option,int facility)379 openlog(const char *ident, int option, int facility)
380 {
381 	const size_t buf_max_size = 256;
382 	char buf[buf_max_size];
383 
384 	syslog_ident = strdup(ident);
385 	/* Concatenate ident + syslog_filename_suffix */
386 	memccpy(buf, ident, '\0',
387 		buf_max_size - strlen(syslog_filename_suffix) - 1);
388 	strncpy(buf + strlen(ident), syslog_filename_suffix,
389 		strlen(syslog_filename_suffix) + 1);
390 
391 	if (syslog_file)
392 		fclose(syslog_file);
393 
394 	syslog_file = fopen(buf, "a");
395 }
396 
397 void
syslog(int priority,const char * format,...)398 syslog(int priority, const char *format, ...)
399 {
400 	const char *priority_string[] = { "", "alert", "",
401 					  "error", "warning", "notice" };
402 	const size_t line_max_size = 512;
403 	char line[line_max_size];
404 	int rc;
405 	time_t now;
406 	struct tm *dt;
407 	va_list ap;
408 
409 	time(&now);
410 	dt = localtime(&now);
411 
412 	rc = snprintf(line, line_max_size,
413 		      "%04d-%02d-%02d %02d:%02d:%02d %s(%s): ",
414 		      dt->tm_year + 1900, dt->tm_mon + 1, dt->tm_mday,
415 		      dt->tm_hour, dt->tm_min, dt->tm_sec,
416 		      syslog_ident, priority_string[priority]);
417 
418 	va_start(ap, format);
419 	vsnprintf(line + rc, line_max_size - rc, format, ap);
420 	va_end(ap);
421 
422 	if (syslog_file) {
423 		fputs(line, syslog_file);
424 		fputs("\n", syslog_file);
425 	}
426 }
427 
428 void
closelog(void)429 closelog(void)
430 {
431 	if (syslog_file)
432 		fclose(syslog_file);
433 	free(syslog_ident);
434 }
435 
436 #endif /* HAVE_SYSLOG_H */
437