1 /* $Header: /var/cvs/mbdyn/mbdyn/mbdyn-1.0/mbdyn/base/sockdrv.cc,v 1.48 2017/01/12 14:46:10 masarati Exp $ */
2 /*
3  * MBDyn (C) is a multibody analysis code.
4  * http://www.mbdyn.org
5  *
6  * Copyright (C) 1996-2017
7  *
8  * Pierangelo Masarati	<masarati@aero.polimi.it>
9  * Paolo Mantegazza	<mantegazza@aero.polimi.it>
10  *
11  * Dipartimento di Ingegneria Aerospaziale - Politecnico di Milano
12  * via La Masa, 34 - 20156 Milano, Italy
13  * http://www.aero.polimi.it
14  *
15  * Changing this copyright notice is forbidden.
16  *
17  * This program is free software; you can redistribute it and/or modify
18  * it under the terms of the GNU General Public License as published by
19  * the Free Software Foundation (version 2 of the License).
20  *
21  *
22  * This program is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25  * GNU General Public License for more details.
26  *
27  * You should have received a copy of the GNU General Public License
28  * along with this program; if not, write to the Free Software
29  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
30  */
31 
32 #include "mbconfig.h"           /* This goes first in every *.c,*.cc file */
33 #include "dataman.h"
34 #include "sockdrv.h"
35 
36 #ifdef USE_SOCKET
37 
38 #include <stdio.h>
39 #include <errno.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <stddef.h>
43 #include <ctype.h>
44 #include <unistd.h>
45 #include <fcntl.h>
46 #include <sys/types.h>
47 #include <sys/socket.h>
48 #include <netinet/in.h>
49 #include <netdb.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <sys/socket.h>
53 #include <netinet/in.h>
54 #include <sys/un.h>
55 #include <arpa/inet.h>
56 
57 #include "sock.h"
58 
59 const size_t USERLEN = 32;
60 const size_t CREDLEN = 128;
61 const size_t BUFSIZE = 1024;
62 const char *MBDynSocketDrivePath = "/var/mbdyn/mbdyn.sock";
63 
SocketDrive(unsigned int uL,const DriveHandler * pDH,unsigned short int p,AuthMethod * a,integer nd,const std::vector<doublereal> & v0)64 SocketDrive::SocketDrive(unsigned int uL, const DriveHandler* pDH,
65 	unsigned short int p, AuthMethod* a,
66 	integer nd, const std::vector<doublereal>& v0)
67 : FileDrive(uL, pDH, "socket", nd, v0),
68 type(AF_INET),
69 auth(a),
70 pFlags(NULL)
71 {
72 	int			save_errno;
73 
74    	ASSERT(p > 0);
75    	ASSERT(auth != NULL);
76    	ASSERT(nd > 0);
77 
78    	/* Create the socket and set it up to accept connections. */
79 	data.Port = p;
80    	sock = mbdyn_make_inet_socket(0, NULL, data.Port, 1, &save_errno);
81    	if (sock == -1) {
82 		const char	*err_msg = strerror(save_errno);
83 
84       		silent_cerr("SocketDrive(" << GetLabel()
85 			<< "): socket failed "
86 			"(" << save_errno << ": "<< err_msg << ")"
87 			<< std::endl);
88       		throw ErrGeneric(MBDYN_EXCEPT_ARGS);
89 
90    	} else if (sock == -2) {
91 		const char	*err_msg = strerror(save_errno);
92 
93       		silent_cerr("SocketDrive(" << GetLabel()
94 			<< "): bind failed "
95 			"(" << save_errno << ": "<< err_msg << ")"
96 			<< std::endl);
97       		throw ErrGeneric(MBDYN_EXCEPT_ARGS);
98    	}
99 
100    	Init();
101 }
102 
SocketDrive(unsigned int uL,const DriveHandler * pDH,const char * path,integer nd,const std::vector<doublereal> & v0)103 SocketDrive::SocketDrive(unsigned int uL, const DriveHandler* pDH,
104 	const char *path,
105 	integer nd, const std::vector<doublereal>& v0)
106 : FileDrive(uL, pDH, "socket", nd, v0),
107 type(AF_LOCAL),
108 auth(NULL),
109 pFlags(NULL)
110 {
111 	int			save_errno;
112 
113    	ASSERT(path != NULL);
114    	ASSERT(nd > 0);
115 
116 	SAFENEW(auth, NoAuth);
117 
118    	/* Create the socket and set it up to accept connections. */
119 	SAFESTRDUP(data.Path, path);
120    	sock = mbdyn_make_named_socket(0, data.Path, 1, &save_errno);
121    	if (sock == -1) {
122 		const char	*err_msg = strerror(save_errno);
123 
124       		silent_cerr("SocketDrive(" << GetLabel()
125 			<< "): socket failed "
126 			"(" << save_errno << ": "<< err_msg << ")"
127 			<< std::endl);
128       		throw ErrGeneric(MBDYN_EXCEPT_ARGS);
129 
130    	} else if (sock == -2) {
131 		const char	*err_msg = strerror(save_errno);
132 
133       		silent_cerr("SocketDrive(" << GetLabel()
134 			<< "): bind failed "
135 			"(" << save_errno << ": "<< err_msg << ")"
136 			<< std::endl);
137       		throw ErrGeneric(MBDYN_EXCEPT_ARGS);
138    	}
139 
140 	Init();
141 }
142 
143 void
Init(void)144 SocketDrive::Init(void)
145 {
146    	/* non-blocking */
147    	int oldflags = fcntl(sock, F_GETFL, 0);
148    	if (oldflags == -1) {
149 		silent_cerr("SocketDrive(" << GetLabel()
150 				<< ": unable to get socket flags"
151 				<< std::endl);
152       		throw ErrGeneric(MBDYN_EXCEPT_ARGS);
153    	}
154    	oldflags |= O_NONBLOCK;
155    	if (fcntl(sock, F_SETFL, oldflags) == -1) {
156 		silent_cerr("SocketDrive(" << GetLabel()
157 				<< ": unable to set socket flags"
158 				<< std::endl);
159       		throw ErrGeneric(MBDYN_EXCEPT_ARGS);
160    	}
161 
162    	if (listen(sock, 1) < 0) {
163       		silent_cerr("SocketDrive(" << GetLabel()
164 			<< "): listen failed" << std::endl);
165       		throw ErrGeneric(MBDYN_EXCEPT_ARGS);
166    	}
167 
168    	SAFENEWARR(pFlags, int, iNumDrives + 1);
169    	for (int iCnt = 0; iCnt <= iNumDrives; iCnt++) {
170       		pFlags[iCnt] = SocketDrive::DEFAULT;
171    	}
172 }
173 
174 
~SocketDrive(void)175 SocketDrive::~SocketDrive(void)
176 {
177    	/* some shutdown stuff ... */
178    	shutdown(sock, SHUT_RDWR /* 2 */ );
179 
180 	switch (type) {
181 	case AF_LOCAL:
182 		if (data.Path) {
183 			unlink(data.Path);
184 			SAFEDELETEARR(data.Path);
185 		}
186 		break;
187 
188 	default:
189 		NO_OP;
190 		break;
191 	}
192 
193    	if (auth != NULL) {
194       		SAFEDELETE(auth);
195    	}
196 }
197 
198 static char *
get_line(char * buf,size_t bufsize,FILE * fd)199 get_line(char *buf, size_t bufsize,  FILE *fd)
200 {
201    	int len;
202 
203    	if (fgets(buf, bufsize, fd) == NULL) {
204       		return NULL;
205   	}
206 
207    	len = strlen(buf);
208    	if (len > 0 && buf[len-1] == '\n') {
209       		buf[len-1] = '\0';
210       		if (len > 1 && buf[len-2] == '\r') {
211 	 		buf[len-2] = '\0';
212       		}
213    	} else {
214       		fprintf(stderr, "buffer overflow\n");
215       		return NULL;
216    	}
217 
218    	return buf;
219 }
220 
221 /*
222  * in/out:
223  *     user : puntatore a buffer di 9 bytes (8+'\0')
224  *     cred : puntatore a buffer di 129 bytes (128+'\0')
225  *
226  * out:
227  *     buf : puntatore a buffer statico che contiene una copia della nuova
228  *           linea nel caso sia stata accidentalmente letta
229  */
230 int
get_auth_token(FILE * fd,char * user,char * cred,char ** nextline)231 get_auth_token(FILE *fd, char *user, char *cred, char **nextline)
232 {
233    	char buf[BUFSIZE];
234 
235    	if (get_line(buf, BUFSIZE, fd) == NULL) {
236       		return -1;
237    	}
238 
239    	user[0] = '\0';
240    	cred[0] = '\0';
241 
242    	if (strncasecmp(buf, "user:", 5) != 0) {
243       		*nextline = (char *)malloc(sizeof(char)*(strlen(buf)+1));
244 		if ((*nextline) == NULL) {
245 	 		return -1;
246       		}
247       		strcpy(*nextline, buf);
248       		return 0;
249    	} else {
250       		char *p;
251       		unsigned int i;
252 
253       		p = buf+5;
254       		while (isspace(*p)) {
255 	 		p++;
256       		}
257       		for (i = 0; i < USERLEN; i++) {
258 	 		if ((user[i] = p[i]) == '\0' || isspace(user[i])) {
259 	    			break;
260 	 		}
261       		}
262       		user[i] = '\0';
263    	}
264 
265    	if (get_line(buf, BUFSIZE, fd) == NULL) {
266       		return -1;
267    	}
268 
269    	if (strncasecmp(buf, "password:", 9) != 0) {
270       		*nextline = (char *)malloc(sizeof(char)*(strlen(buf)+1));
271 		if ((*nextline) == NULL) {
272 	 		return -1;
273       		}
274       		strcpy(*nextline, buf);
275       		return 0;
276    	} else {
277       		char *p;
278       		unsigned int i;
279 
280       		p = buf+9;
281       		while (isspace(*p)) {
282 	 		p++;
283       		}
284       		for (i = 0; i < CREDLEN; i++) {
285 	 		if ((cred[i] = p[i]) == '\0' || isspace(cred[i])) {
286 	    			break;
287 	 		}
288       		}
289       		cred[i] = '\0';
290    	}
291 
292    	return 1;
293 }
294 
295 void
ServePending(const doublereal &)296 SocketDrive::ServePending(const doublereal& /* t */ )
297 {
298    	struct sockaddr_in client_name;
299    	socklen_t socklen;
300 
301    	/* prova */
302    	for (integer iCnt = 1; iCnt <= iNumDrives; iCnt++) {
303       		if (pFlags[iCnt] & SocketDrive::IMPULSIVE) {
304 	 		pdVal[iCnt] = 0.;
305       		}
306    	}
307 
308    	while (true) {
309       		int got_value = 0;
310       		char *nextline = NULL;
311       		const size_t bufsize = BUFSIZE;
312       		char buf[bufsize];
313 
314       		int label;
315       		doublereal value;
316 
317    		int cur_sock = accept(sock,
318 				  (struct sockaddr *)&client_name,
319 				  &socklen);
320 
321       		if (cur_sock == -1) {
322 			int save_errno = errno;
323 	 		if (save_errno != EWOULDBLOCK) {
324 	    			silent_cerr(
325 					"SocketDrive(" << GetLabel() << "): "
326 					"accept failed "
327 					"(" << save_errno << ": "
328 					<< strerror(save_errno) << ")"
329 					<< std::endl);
330 	 		}
331 	 		return;
332       		}
333 
334       		silent_cout("SocketDrive(" << GetLabel() << "): "
335 			"connect from " << inet_ntoa(client_name.sin_addr)
336 		  	<< ":" << ntohs(client_name.sin_port) << std::endl);
337 
338 		bool bAuthc = false;
339 #ifdef HAVE_SASL2
340 		if (dynamic_cast<SASL2_Auth*>(auth)) {
341      	 		if (auth->Auth(cur_sock) != AuthMethod::AUTH_OK) {
342 		 		silent_cerr(
343 					"SocketDrive(" << GetLabel() << "): "
344 					"authentication failed" << std::endl);
345 		 		continue;
346       			}
347 			bAuthc = true;
348 		}
349 #endif /* HAVE_SASL2 */
350 
351    		FILE* fd = fdopen(cur_sock, "r");
352 
353 		if (!bAuthc) {
354       			char user[USERLEN + 1];
355       			char cred[CREDLEN + 1];
356 
357       			if (get_auth_token(fd, user, cred, &nextline) == -1) {
358 	 			silent_cerr(
359 					"SocketDrive(" << GetLabel() << "): "
360 					"corrupted stream" << std::endl);
361 	 			fclose(fd);
362 	 			continue;
363       			}
364 
365       			DEBUGCOUT("got auth token: user=\"" << user
366 				<< "\", cred=\"" << cred << "\"" << std::endl);
367 
368       			if (auth->Auth(user, cred) != AuthMethod::AUTH_OK) {
369 	 			silent_cerr(
370 					"SocketDrive(" << GetLabel() << "): "
371 					"authentication failed" << std::endl);
372 	 			fclose(fd);
373 	 			continue;
374       			}
375 		}
376 
377       		DEBUGCOUT("authenticated" << std::endl);
378 
379       		/*
380 		 * la nuova linea puo' essere gia' stata letta
381 		 * da get_auth_token
382 		 */
383       		if (nextline == NULL) {
384 	 		if (get_line(buf, bufsize, fd) == NULL) {
385 	    			silent_cerr(
386 					"SocketDrive(" << GetLabel() << "): "
387 					"corrupted stream" << std::endl);
388 	    			fclose(fd);
389 	    			continue;
390 	 		}
391       		} else {
392 	 		strncpy(buf, nextline, bufsize);
393 	 		free(nextline);
394       		}
395       		nextline = buf;
396 
397       		/* legge la label */
398       		if (strncasecmp(nextline, "label:", 6) != 0) {
399 	 		silent_cerr("SocketDrive(" << GetLabel() << "): "
400 				"missing label" << std::endl);
401 	 		fclose(fd);
402 	 		continue;
403       		}
404 
405 	 	char *p = nextline + 6;
406 	 	while (isspace(p[0])) {
407 	    		p++;
408 	 	}
409 
410 	 	if (sscanf(p, "%d", &label) != 1) {
411 	    		silent_cerr("SocketDrive(" << GetLabel() << "): "
412 				"unable to read label" << std::endl);
413 	    		fclose(fd);
414 	    		continue;
415 	 	}
416 
417 	 	if (label <= 0 || label > iNumDrives) {
418 	    		silent_cerr("SocketDrive(" << GetLabel() << "): "
419 				"illegal label " << label << std::endl);
420 	    		fclose(fd);
421 	    		continue;
422 	 	}
423 
424 	 	while (true) {
425 	    		if (get_line(buf, bufsize, fd) == NULL) {
426 	       			silent_cerr(
427 					"SocketDrive(" << GetLabel() << "): "
428 					"corrupted stream" << std::endl);
429 	       			fclose(fd);
430 	       			break;
431 	    		}
432 
433 	    		nextline = buf;
434 
435 	    		if (nextline[0] == '.') {
436 	       			fclose(fd);
437 	       			break;
438 	    		}
439 
440 	    		if (strncasecmp(nextline, "value:", 6) == 0) {
441 	       			char *p = nextline+6;
442 	       			while (isspace(p[0])) {
443 	 				p++;
444 				}
445 
446 	       			if (sscanf(p, "%lf", &value) != 1) {
447 	  				silent_cerr("SocketDrive(" << GetLabel() << "): "
448 						"unable to read value"
449 						<< std::endl);
450 	  				fclose(fd);
451 	  				break;
452 				}
453 				got_value = 1;
454 
455     			} else if (strncasecmp(nextline, "inc:", 4) == 0) {
456        				char *p = nextline+4;
457        				while (isspace(p[0])) {
458 	  				p++;
459        				}
460 
461        				if (strncasecmp(p, "yes", 3) == 0) {
462 	  				pFlags[label] |= SocketDrive::INCREMENTAL;
463        				} else if (strncasecmp(p, "no", 2) == 0) {
464 	  				pFlags[label] &= !SocketDrive::INCREMENTAL;
465        				} else {
466 	  				silent_cerr("SocketDrive(" << GetLabel() << "): "
467 						"\"inc\" line in "
468 						"\"" << nextline << "\" "
469 						"looks corrupted"
470 						<< std::endl);
471 	  				fclose(fd);
472 	  				break;
473        				}
474        				nextline = NULL;
475 
476     			} else if (strncasecmp(nextline, "imp:", 4) == 0) {
477        				char *p = nextline+4;
478        				while (isspace(p[0])) {
479 	  				p++;
480        				}
481 
482        				if (strncasecmp(p, "yes", 3) == 0) {
483 	  				pFlags[label] |= SocketDrive::IMPULSIVE;
484        				} else if (strncasecmp(p, "no", 2) == 0) {
485 	  				pFlags[label] &= !SocketDrive::IMPULSIVE;
486        				} else {
487 	  				silent_cerr("SocketDrive(" << GetLabel() << "): "
488 						"\"imp\" line" " in "
489 						"\"" << nextline << "\""
490 						" looks corrupted"
491 						<< std::endl);
492 	  				fclose(fd);
493 	  				break;
494        				}
495        				nextline = NULL;
496     			}
497 
498 	 		/* usa i valori */
499 	 		if (got_value) {
500 	    			if (pFlags[label] & SocketDrive::INCREMENTAL) {
501 	       				silent_cout("SocketDrive(" << GetLabel() << "): "
502 						"adding " << value
503 						<< " to label " << label
504 						<< std::endl);
505 	       				pdVal[label] += value;
506 
507 	    			} else {
508 	       				silent_cout("SocketDrive(" << GetLabel() << "): "
509 						"setting label " << label
510 						<< " to value " << value
511 						<< std::endl);
512 	       				pdVal[label] = value;
513 	    			}
514 	 		}
515       		}
516    	}
517 }
518 
519 /* Scrive il contributo del DriveCaller al file di restart */
520 std::ostream&
Restart(std::ostream & out) const521 SocketDrive::Restart(std::ostream& out) const
522 {
523    	return out << "SocketDrive not implemented yet" << std::endl;
524 }
525 
526 /* legge i drivers tipo socket */
527 
528 Drive *
Read(unsigned uLabel,const DataManager * pDM,MBDynParser & HP)529 SocketDR::Read(unsigned uLabel, const DataManager *pDM, MBDynParser& HP)
530 {
531 	Drive* pDr = NULL;
532 
533 	integer idrives = HP.GetInt();
534 	unsigned short int port = MBDynSocketDrivePort;
535 	const char *path = NULL;
536 
537 	std::vector<doublereal> v0;
538 	if (HP.IsKeyWord("initial" "values")) {
539 		v0.resize(idrives);
540 		for (integer i = 0; i < idrives; i++) {
541 			v0[i] = HP.GetReal();
542 		}
543 	}
544 
545 	if (HP.IsKeyWord("local")) {
546 		path = HP.GetFileName();
547 		ASSERT(path != NULL);
548 
549 	} else if (HP.IsKeyWord("port")) {
550 		port = HP.GetInt();
551 #ifdef IPPORT_USERRESERVED
552 		if (port < IPPORT_USERRESERVED) {
553 			silent_cerr("SocketDrive(" << uLabel << "): "
554 					"cannot listen on port " << port
555 					<< ": less than IPPORT_USERRESERVED="
556 					<< IPPORT_USERRESERVED
557 					<< " at line " << HP.GetLineData()
558 					<< std::endl);
559 			throw ErrGeneric(MBDYN_EXCEPT_ARGS);
560 		}
561 		/* if #undef'd, don't bother checking;
562 		 * the OS will do it for us */
563 #endif /* IPPORT_USERRESERVED */
564 	}
565 
566 	if (path == NULL) {
567 		AuthMethod* pAuth = ReadAuthMethod(pDM, HP);
568 
569 		if (pAuth == NULL) {
570 			silent_cerr("need an authentication method "
571 				"at line " << HP.GetLineData()
572 				<< std::endl);
573 			throw ErrGeneric(MBDYN_EXCEPT_ARGS);
574 		}
575 
576 		SAFENEWWITHCONSTRUCTOR(pDr,
577 			SocketDrive,
578 			SocketDrive(uLabel, pDM->pGetDrvHdl(),
579 				port, pAuth, idrives, v0));
580 
581 	} else {
582 		SAFENEWWITHCONSTRUCTOR(pDr,
583 			SocketDrive,
584 			SocketDrive(uLabel, pDM->pGetDrvHdl(),
585 				path, idrives, v0));
586 	}
587 
588 	return pDr;
589 }
590 
591 #endif /* USE_SOCKET */
592