Added support for BIONIC libc to allow compilation on android toolchain.
[vde.git] / vde-2 / src / lib / libvdeplug.c
blobe3623d91a652e305485ed280e14cd94d3ad7d97b
1 /*
2 * libvdeplug - A library to connect to a VDE Switch.
3 * Copyright (C) 2006 Renzo Davoli, University of Bologna
4 * (c) 2010 Renzo Davoli - stream + point2point
5 * (c) 2011 Renzo Davoli - udpconnect
7 * This library is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation version 2.1 of the License, or (at
10 * your option) any later version.
12 * This library is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
15 * General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include <errno.h>
23 #include <sys/socket.h>
24 #include <sys/un.h>
25 #include <stdint.h>
26 #include <stdio.h>
27 #include <limits.h>
28 #include <stdlib.h>
29 #include <pwd.h>
30 #include <grp.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <sys/uio.h>
34 #include <unistd.h>
35 #include <string.h>
36 #include <netdb.h>
37 #include <netinet/in.h>
39 #include <config.h>
40 #include <vde.h>
41 #include <vdecommon.h>
43 #include <libvdeplug.h>
44 #define CONNECTED_P2P
46 /* Per-User standard switch definition */
47 /* This will be prefixed by getenv("HOME") */
48 /* it can be a symbolic link to the switch dir */
49 #define STDSWITCH "/.vde2/default.switch"
50 /* deprecated old name */
51 #define STDSOCK "/.vde2/stdsock"
53 #ifdef USE_IPN
54 #if 0
55 /* AF_IPN has not been officially assigned yet
56 we "steal" unused AF_NETBEUI in the meanwhile
57 this code will be uncommented when AF_IPN is assigned. */
58 #ifndef AF_IPN
59 #define AF_IPN 0 /* IPN sockets: */
60 #define PF_IPN AF_IPN
61 #endif
62 #endif
63 #ifndef AF_NETBEUI
64 #ifdef PF_NETBEUI
65 #define AF_NETBEUI PF_NETBEUI
66 #else
67 #define AF_NETBEUI 13
68 #endif
69 #endif
70 #define AF_IPN_STOLEN AF_NETBEUI /* IPN temporary sockets */
71 #define PF_IPN_STOLEN AF_IPN_STOLEN
72 #define IPN_ANY 0
74 #define IPN_SO_PORT 0
75 #define IPN_SO_DESCR 1
76 #endif
78 #ifndef MIN
79 #define MIN(X,Y) (((X)<(Y))?(X):(Y))
80 #endif
82 /* Fallback names for the control socket, NULL-terminated array of absolute
83 * filenames. */
84 char *fallback_sockname[] = {
85 "/var/run/vde.ctl/ctl",
86 "/tmp/vde.ctl/ctl",
87 "/tmp/vde.ctl",
88 NULL,
91 /* Fallback directories for the data socket, NULL-terminated array of absolute
92 * directory names, with no trailing /. */
93 const char *fallback_dirname[] = {
94 "/var/run",
95 "/var/tmp",
96 "/tmp",
97 NULL,
100 struct vdeconn {
101 int fdctl;
102 int fddata;
103 char *inpath;
104 size_t outlen;
105 struct sockaddr *outsock;
108 #define SWITCH_MAGIC 0xfeedface
109 #define MAXDESCR 128
110 #define VDEFLAG_P2P_SOCKET 1
111 #define VDEFLAG_UDP_SOCKET 2
112 #define VDEFLAG_P2P (VDEFLAG_P2P_SOCKET | VDEFLAG_UDP_SOCKET)
114 enum request_type { REQ_NEW_CONTROL, REQ_NEW_PORT0 };
116 struct request_v3 {
117 uint32_t magic;
118 uint32_t version;
119 enum request_type type;
120 struct sockaddr_un sock;
121 char description[MAXDESCR];
122 } __attribute__((packed));
124 VDECONN *vde_open_real(char *given_sockname, char *descr,int interface_version,
125 struct vde_open_args *open_args)
127 struct vdeconn *conn=NULL;
128 struct passwd *callerpwd;
129 struct request_v3 req;
130 int pid = getpid();
131 int port=0;
132 char *group=NULL;
133 mode_t mode=0700;
134 int sockno=0;
135 int flags=0;
136 int res;
137 char *std_sockname=NULL;
138 char *real_sockname=NULL;
139 char *sockname=NULL;
140 char *ssh_client = getenv("SSH_CLIENT");
141 int descrlen;
143 if (open_args != NULL) {
144 if (interface_version == 1) {
145 port=open_args->port;
146 group=open_args->group;
147 mode=open_args->mode;
148 if (port == -1)
149 flags |= VDEFLAG_P2P_SOCKET;
151 else {
152 errno=EINVAL;
153 goto abort;
157 memset(&req, 0, sizeof(req));
158 if ((std_sockname=(char *)calloc(PATH_MAX,sizeof(char)))==NULL) {
159 errno=ENOMEM;
160 goto abort;
162 if ((real_sockname=(char *)calloc(PATH_MAX,sizeof(char)))==NULL) {
163 errno=ENOMEM;
164 goto abort;
166 sockname = real_sockname;
167 if ((conn=calloc(1,sizeof(struct vdeconn)))==NULL)
169 errno=ENOMEM;
170 goto abort;
172 conn->fdctl=conn->fddata=-1;
173 //get the login name
174 callerpwd=getpwuid(getuid());
175 req.type = REQ_NEW_CONTROL;
176 if (given_sockname == NULL || *given_sockname == '\0') {
177 char *homedir = getenv("HOME");
178 given_sockname = NULL;
179 if (homedir) {
180 struct stat statbuf;
181 snprintf(std_sockname, PATH_MAX, "%s%s", homedir, STDSWITCH);
182 if (lstat(std_sockname,&statbuf)==0)
183 given_sockname = std_sockname;
184 else {
185 snprintf(std_sockname, PATH_MAX, "%s%s", homedir, STDSOCK);
186 if (lstat(std_sockname,&statbuf)==0)
187 given_sockname = std_sockname;
190 } else {
191 char *split;
192 if((split = strstr(given_sockname,"->")) != NULL && strrchr(split,':') != NULL)
193 flags |= VDEFLAG_UDP_SOCKET;
194 else if(given_sockname[strlen(given_sockname)-1] == ']'
195 && (split=strrchr(given_sockname,'[')) != NULL) {
196 *split=0;
197 split++;
198 port=atoi(split);
199 if (*split==']')
200 flags |= VDEFLAG_P2P_SOCKET;
201 else if (port == 0)
202 req.type = REQ_NEW_PORT0;
203 if (*given_sockname==0)
204 given_sockname = NULL;
208 /* Canonicalize the sockname: we need to send an absolute pathname to the
209 * switch (we don't know its cwd) for the data socket. Appending
210 * given_sockname to getcwd() would be enough, but we could end up with a
211 * name longer than PATH_MAX that couldn't be used as sun_path. */
212 if (given_sockname && !(flags & VDEFLAG_P2P) &&
213 vde_realpath(given_sockname, real_sockname) == NULL)
214 goto abort;
216 #ifdef USE_IPN
217 #if 0
218 /* AF_IPN has not been officially assigned yet
219 we "steal" unused AF_NETBEUI in the meanwhile
220 this code will be uncommented when AF_IPN is assigned. */
221 if((conn->fddata = socket(AF_IPN,SOCK_RAW,IPN_ANY)) >= 0) {
222 /* IPN service exists */
223 sockun.sun_family = AF_IPN;
225 #endif
226 if((flags & VDEFLAG_P2P) == 0 &&
227 (conn->fddata = socket(AF_IPN_STOLEN,SOCK_RAW,IPN_ANY)) >= 0) {
228 struct sockaddr_un sockun;
229 memset(&sockun, 0, sizeof(sockun));
230 /* IPN_STOLEN service exists */
231 sockun.sun_family = AF_IPN_STOLEN;
232 if (port != 0 || req.type == REQ_NEW_PORT0)
233 setsockopt(conn->fddata,0,IPN_SO_PORT,&port,sizeof(port));
234 /* If we're given a sockname, just try it */
235 if (given_sockname)
237 snprintf(sockun.sun_path, sizeof(sockun.sun_path), "%s", sockname);
238 res = connect(conn->fddata, (struct sockaddr *) &sockun, sizeof(sockun));
240 /* Else try all the fallback socknames, one by one */
241 else
243 int i;
244 for (i = 0, res = -1; fallback_sockname[i] && (res != 0); i++)
246 snprintf(sockun.sun_path, sizeof(sockun.sun_path), "%s", fallback_sockname[i]);
247 res = connect(conn->fddata, (struct sockaddr *) &sockun, sizeof(sockun));
251 /* If one of the connect succeeded, we're done */
252 if (res == 0)
254 int descrlen=snprintf(req.description,MAXDESCR,"%s user=%s PID=%d",
255 descr,(callerpwd != NULL)?callerpwd->pw_name:"??",
256 pid);
257 if (ssh_client) {
258 char *endofip=strchr(ssh_client,' ');
259 if (endofip) *endofip=0;
260 snprintf(req.description+descrlen,MAXDESCR-descrlen,
261 " SSH=%s", ssh_client);
262 if (endofip) *endofip=' ';
264 setsockopt(conn->fddata,0,IPN_SO_DESCR,req.description,
265 strlen(req.description+1));
266 conn->fdctl=-1;
267 goto cleanup;
268 } else
269 close(conn->fddata);
271 #endif
272 /* UDP connection */
273 if (flags & VDEFLAG_UDP_SOCKET) {
274 struct addrinfo hints;
275 struct addrinfo *result,*rp;
276 int s;
277 char *dst=strstr(given_sockname,"->");
278 char *src=given_sockname;
279 char *srcport;
280 char *dstport;
281 memset(&hints,0,sizeof(hints));
282 hints.ai_socktype=SOCK_DGRAM;
283 *dst=0;
284 dst+=2;
285 dstport=strrchr(dst,':');
286 if (dstport==NULL) {
287 errno=EINVAL;
288 goto abort;
290 *dstport=0;
291 dstport++;
292 srcport=strrchr(src,':');
293 if (srcport==NULL) {
294 srcport=src;
295 src=NULL;
297 //fprintf(stderr,"UDP!%s:%s -> %s:%s \n",src,srcport,dst,dstport);
298 hints.ai_flags = AI_PASSIVE;
299 s = getaddrinfo(src, srcport, &hints, &result);
301 if (s != 0) {
302 errno=ECONNABORTED;
303 goto abort;
306 for (rp = result; rp != NULL; rp = rp->ai_next) {
307 conn->fddata = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
308 if (conn->fddata == -1)
309 continue;
311 if (bind(conn->fddata, rp->ai_addr, rp->ai_addrlen) == 0)
312 break; /* Success */
314 close(conn->fddata);
317 if (rp == NULL) {
318 errno=ECONNABORTED;
319 goto abort;
322 freeaddrinfo(result);
323 hints.ai_flags = 0;
325 s = getaddrinfo(dst, dstport, &hints, &result);
327 if (s != 0) {
328 errno=ECONNABORTED;
329 goto abort;
331 /* for now it takes the first */
332 conn->outlen = result->ai_addrlen;
333 conn->outsock = malloc(result->ai_addrlen);
334 memcpy(conn->outsock, result->ai_addr, result->ai_addrlen);
336 freeaddrinfo(result);
338 goto cleanup;
340 /* define a female socket for point2point connection */
341 if (flags & VDEFLAG_P2P_SOCKET) {
342 struct stat sockstat;
343 struct sockaddr_un sockun;
344 struct sockaddr_un *sockout;
345 memset(&sockun, 0, sizeof(sockun));
346 if(given_sockname == NULL) {
347 errno = EINVAL;
348 goto abort;
350 strcpy(sockname,given_sockname); /* XXX canonicalize should be better */
351 if((conn->fddata = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)
352 goto abort;
353 sockun.sun_family = AF_UNIX;
354 memset(sockun.sun_path,0,sizeof(sockun.sun_path));
355 snprintf(sockun.sun_path, sizeof(sockun.sun_path)-1, "%s", sockname);
356 /* the socket already exists */
357 if(stat(sockun.sun_path,&sockstat) == 0) {
358 if (S_ISSOCK(sockstat.st_mode)) {
359 /* the socket is already in use */
360 res = connect(conn->fddata, (struct sockaddr *) &sockun, sizeof(sockun));
361 if (res >= 0) {
362 errno = EADDRINUSE;
363 goto abort;
365 if (errno == ECONNREFUSED)
366 unlink(sockun.sun_path);
369 res = bind(conn->fddata, (struct sockaddr *) &sockun, sizeof(sockun));
370 if (res < 0)
371 goto abort;
372 conn->inpath=strdup(sockun.sun_path);
373 conn->outlen = sizeof(struct sockaddr_un);
374 conn->outsock = (struct sockaddr *) (sockout = calloc(1,sizeof(struct sockaddr_un)));
375 if (conn->outsock ==NULL)
376 goto abort;
377 sockout->sun_family = AF_UNIX;
378 snprintf(sockout->sun_path, sizeof(sockun.sun_path), "%s+", sockname);
379 if (group) {
380 struct group *gs;
381 gid_t gid;
382 if ((gs=getgrnam(group)) == NULL)
383 gid=atoi(group);
384 else
385 gid=gs->gr_gid;
386 chown(sockun.sun_path,-1,gid);
388 chmod(sockun.sun_path,mode);
389 goto cleanup;
390 } else {
391 struct sockaddr_un sockun;
392 struct sockaddr_un dataout;
393 memset(&sockun, 0, sizeof(sockun));
394 memset(&dataout, 0, sizeof(dataout));
396 /* connection to a vde_switch */
397 if((conn->fdctl = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
398 goto abort;
399 if((conn->fddata = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)
400 goto abort;
401 sockun.sun_family = AF_UNIX;
403 /* If we're given a sockname, just try it (remember: sockname is the
404 * canonicalized version of given_sockname - though we don't strictly need
405 * the canonicalized versiono here). sockname should be the name of a
406 * *directory* which contains the control socket, named ctl. Older
407 * versions of VDE used a socket instead of a directory (so an additional
408 * attempt with %s instead of %s/ctl could be made), but they should
409 * really not be used anymore. */
410 if (given_sockname)
412 snprintf(sockun.sun_path, sizeof(sockun.sun_path), "%s/ctl", sockname);
413 res = connect(conn->fdctl, (struct sockaddr *) &sockun, sizeof(sockun));
415 /* Else try all the fallback socknames, one by one */
416 else
418 int i;
419 for (i = 0, res = -1; fallback_sockname[i] && (res != 0); i++)
421 /* Remember sockname for the data socket directory */
422 sockname = fallback_sockname[i];
423 snprintf(sockun.sun_path, sizeof(sockun.sun_path), "%s", sockname);
424 res = connect(conn->fdctl, (struct sockaddr *) &sockun, sizeof(sockun));
428 if (res != 0) {
429 struct stat sockstat;
430 /* define a male plug for point2point connection */
431 if (!given_sockname)
432 goto abort;
433 snprintf(sockun.sun_path, sizeof(sockun.sun_path), "%s", sockname);
434 res = connect(conn->fddata, (struct sockaddr *) &sockun, sizeof(sockun));
435 if (res < 0)
436 goto abort;
437 snprintf(sockun.sun_path, sizeof(sockun.sun_path), "%s+", sockname);
438 if(stat(sockun.sun_path,&sockstat) == 0) {
439 if (S_ISSOCK(sockstat.st_mode)) {
440 /* the socket is already in use */
441 res = connect(conn->fddata, (struct sockaddr *) &sockun, sizeof(sockun));
442 if (res >= 0) {
443 errno = EADDRINUSE;
444 goto abort;
446 if (errno == ECONNREFUSED)
447 unlink(sockun.sun_path);
450 res = bind(conn->fddata, (struct sockaddr *) &sockun, sizeof(sockun));
451 if (res < 0)
452 goto abort;
453 conn->inpath=strdup(sockun.sun_path);
454 if (group) {
455 struct group *gs;
456 gid_t gid;
457 if ((gs=getgrnam(group)) == NULL)
458 gid=atoi(group);
459 else
460 gid=gs->gr_gid;
461 chown(sockun.sun_path,-1,gid);
463 chmod(sockun.sun_path,mode);
464 close(conn->fdctl);
465 conn->fdctl=-1;
466 goto cleanup;
469 req.magic=SWITCH_MAGIC;
470 req.version=3;
471 req.type=req.type+(port << 8);
472 req.sock.sun_family=AF_UNIX;
474 /* First choice, store the return socket from the switch in the control
475 * dir. We assume that given_sockname (hence sockname) is a directory.
476 * Should be a safe assumption unless someone modifies the previous group
477 * of connect() attempts (see the comments above for more information). */
478 memset(req.sock.sun_path, 0, sizeof(req.sock.sun_path));
481 /* Here sockname is the last successful one in the previous step. */
482 sprintf(req.sock.sun_path, "%s/.%05d-%05d", sockname, pid, sockno++);
483 res=bind(conn->fddata, (struct sockaddr *) &req.sock, sizeof (req.sock));
485 while (res < 0 && errno == EADDRINUSE);
487 /* It didn't work. So we cycle on the fallback directories until we find a
488 * suitable one (or the list ends). */
489 if (res < 0)
491 int i;
492 for (i = 0, res = -1; fallback_dirname[i] && (res != 0); i++)
494 memset(req.sock.sun_path, 0, sizeof(req.sock.sun_path));
497 sprintf(req.sock.sun_path, "%s/vde.%05d-%05d", fallback_dirname[i], pid, sockno++);
498 res = bind(conn->fddata, (struct sockaddr *) &req.sock, sizeof (req.sock));
500 while (res < 0 && errno == EADDRINUSE);
504 /* Nothing worked, so cleanup and return with an error. */
505 if (res < 0)
506 goto abort;
508 conn->inpath=strdup(req.sock.sun_path);
510 if (group) {
511 struct group *gs;
512 gid_t gid;
513 if ((gs=getgrnam(group)) == NULL)
514 gid=atoi(group);
515 else
516 gid=gs->gr_gid;
517 chown(req.sock.sun_path,-1,gid);
518 } else {
519 /* when group is not defined, set permission for the reverse channel */
520 struct stat ctlstat;
521 /* if no permission gets "voluntarily" granted to the socket */
522 if ((mode & 077) == 0) {
523 if (stat(sockun.sun_path, &ctlstat) == 0) {
524 /* if the switch is owned by root or by the same user it should
525 work 0700 */
526 if (ctlstat.st_uid != 0 && ctlstat.st_uid != geteuid()) {
527 /* try to change the group ownership to the same of the switch */
528 /* this call succeeds if the vde user and the owner of the switch
529 belong to the group */
530 if (chown(req.sock.sun_path,-1,ctlstat.st_gid) == 0)
531 mode |= 070;
532 else
533 mode |= 077;
538 chmod(req.sock.sun_path,mode);
540 #ifdef DESCR_INCLUDE_SOCK
541 descrlen=snprintf(req.description,MAXDESCR,"%s user=%s PID=%d SOCK=%s",
542 descr,(callerpwd != NULL)?callerpwd->pw_name:"??",
543 pid,req.sock.sun_path);
544 #else
545 descrlen=snprintf(req.description,MAXDESCR,"%s user=%s PID=%d",
546 descr,(callerpwd != NULL)?callerpwd->pw_name:"??", pid);
547 #endif
549 if (ssh_client) {
550 char *endofip=strchr(ssh_client,' ');
551 if (endofip) *endofip=0;
552 snprintf(req.description+descrlen,MAXDESCR-descrlen," SSH=%s", ssh_client);
553 if (endofip) *endofip=' ';
557 if (send(conn->fdctl,&req,sizeof(req)-MAXDESCR+strlen(req.description),0)<0)
558 goto abort;
560 if (recv(conn->fdctl,&dataout,sizeof(struct sockaddr_un),0)<0)
561 goto abort;
563 if (connect(conn->fddata,(struct sockaddr *)&dataout,sizeof(struct sockaddr_un))<0)
564 goto abort;
566 chmod(dataout.sun_path,mode);
568 goto cleanup;
571 abort:
573 int err=errno;
574 if (conn) {
575 if (conn->fdctl >= 0)
576 close(conn->fdctl);
577 if (conn->fddata >= 0)
578 close(conn->fddata);
579 if (conn->inpath != NULL)
580 unlink(conn->inpath);
581 if (conn->outsock != NULL)
582 free(conn->outsock);
583 free(conn);
585 conn = NULL;
586 errno=err;
588 cleanup:
590 int err=errno;
591 if (std_sockname) free(std_sockname);
592 if (real_sockname) free(real_sockname);
593 errno = err;
595 return conn;
598 ssize_t vde_recv(VDECONN *conn,void *buf,size_t len,int flags)
600 #ifdef CONNECTED_P2P
601 ssize_t retval;
602 if (__builtin_expect(conn!=0,1)) {
603 if (__builtin_expect(((retval=recv(conn->fddata,buf,len,0)) > 0), 1))
604 return retval;
605 else {
606 if (retval == 0 && conn->outsock != NULL) {
607 static struct sockaddr unspec={AF_UNSPEC};
608 connect(conn->fddata,&unspec,sizeof(unspec));
610 return retval;
613 else {
614 errno=EBADF;
615 return -1;
617 #else
618 if (__builtin_expect(conn!=0,1))
619 return recv(conn->fddata,buf,len,0);
620 else {
621 errno=EBADF;
622 return -1;
624 #endif
627 ssize_t vde_send(VDECONN *conn,const void *buf,size_t len,int flags)
629 #ifdef CONNECTED_P2P
630 if (__builtin_expect(conn!=0,1)) {
631 ssize_t retval;
632 if (__builtin_expect(((retval=send(conn->fddata,buf,len,0)) >= 0),1))
633 return retval;
634 else {
635 if (__builtin_expect(errno == ENOTCONN || errno == EDESTADDRREQ,0)) {
636 if (__builtin_expect(conn->outsock != NULL,1)) {
637 connect(conn->fddata, conn->outsock,conn->outlen);
638 return send(conn->fddata,buf,len,0);
639 } else
640 return retval;
641 } else
642 return retval;
644 } else {
645 errno=EBADF;
646 return -1;
648 #else
649 if (__builtin_expect(conn!=0,1)) {
650 if (__builtin_expect(conn->outsock == NULL,1))
651 return send(conn->fddata,buf,len,0);
652 else
653 return sendto(conn->fddata,buf,len,0,
654 conn->outsock,conn->outlen);
655 } else {
656 errno=EBADF;
657 return -1;
659 #endif
662 int vde_datafd(VDECONN *conn)
664 if (__builtin_expect(conn!=0,1))
665 return conn->fddata;
666 else {
667 errno=EBADF;
668 return -1;
672 int vde_ctlfd(VDECONN *conn)
674 if (__builtin_expect(conn!=0,1))
675 return conn->fdctl;
676 else {
677 errno=EBADF;
678 return -1;
682 int vde_close(VDECONN *conn)
684 if (__builtin_expect(conn!=0,1)) {
685 #ifdef CONNECTED_P2P
686 send(conn->fddata,NULL,0,0);
687 #endif
688 if (conn->inpath != NULL)
689 unlink(conn->inpath);
690 if (conn->outsock != NULL)
691 free(conn->outsock);
692 close(conn->fddata);
693 close(conn->fdctl);
694 free(conn);
695 return 0;
696 } else {
697 errno=EBADF;
698 return -1;
702 /* vdestream */
704 #define MAXPACKET 1521
706 struct vdestream {
707 void *opaque;
708 int fdout;
709 ssize_t (*frecv)(void *opaque, void *buf, size_t count);
710 void (*ferr)(void *opaque, int type, char *format, ...);
711 char fragment[MAXPACKET];
712 char *fragp;
713 unsigned int rnx,remaining;
716 VDESTREAM *vdestream_open(void *opaque,
717 int fdout,
718 ssize_t (*frecv)(void *opaque, void *buf, size_t count),
719 void (*ferr)(void *opaque, int type, char *format, ...)
722 VDESTREAM *vdestream;
723 if ((vdestream=calloc(1,sizeof(struct vdestream)))==NULL) {
724 errno=ENOMEM;
725 return NULL;
726 } else {
727 vdestream->opaque=opaque;
728 vdestream->fdout=fdout;
729 vdestream->frecv=frecv;
730 vdestream->ferr=ferr;
731 return vdestream;
735 ssize_t vdestream_send(VDESTREAM *vdestream, const void *buf, size_t len)
737 if (len <= MAXPACKET) {
738 unsigned char header[2];
739 struct iovec iov[2]={{header,2},{(void *)buf,len}};
740 header[0]=len >> 8;
741 header[1]=len & 0xff;
742 return writev(vdestream->fdout,iov,2);
743 } else
744 return 0;
747 void vdestream_recv(VDESTREAM *vdestream, unsigned char *buf, size_t len)
749 //fprintf(stderr,"%s: splitpacket rnx=%d remaining=%d size=%d\n",myname,rnx,vdestream->remaining,len);
750 if (len==0) return;
751 if (vdestream->rnx>0) {
752 register int amount=MIN(vdestream->remaining,len);
753 //fprintf(stderr,"%s: fragment amount %d\n",myname,amount);
754 memcpy(vdestream->fragp,buf,amount);
755 vdestream->remaining-=amount;
756 vdestream->fragp+=amount;
757 buf+=amount;
758 len-=amount;
759 if (vdestream->remaining==0) {
760 //fprintf(stderr,"%s: delivered defrag %d\n",myname,vdestream->rnx);
761 vdestream->frecv(vdestream->opaque,vdestream->fragment,vdestream->rnx);
762 vdestream->rnx=0;
765 while (len > 1) {
766 vdestream->rnx=(buf[0]<<8)+buf[1];
767 len-=2;
768 //fprintf(stderr,"%s %d: packet %d size %d %x %x\n",myname,getpid(),vdestream->rnx,len,buf[0],buf[1]);
769 buf+=2;
770 if (vdestream->rnx == 0)
771 continue;
772 if (vdestream->rnx > MAXPACKET) {
773 if (vdestream->ferr != NULL)
774 vdestream->ferr(vdestream->opaque,PACKET_LENGTH_ERROR,
775 "size %d expected size %d",len,vdestream->rnx);
776 vdestream->rnx=0;
777 return;
779 if (vdestream->rnx > len) {
780 //fprintf(stderr,"%s: begin defrag %d\n",myname,vdestream->rnx);
781 vdestream->fragp=vdestream->fragment;
782 memcpy(vdestream->fragp,buf,len);
783 vdestream->remaining=vdestream->rnx-len;
784 vdestream->fragp+=len;
785 len=0;
786 } else {
787 //fprintf(stderr,"%s: deliver %d\n",myname,vdestream->rnx);
788 vdestream->frecv(vdestream->opaque,buf,vdestream->rnx);
789 buf+=vdestream->rnx;
790 len-=vdestream->rnx;
791 vdestream->rnx=0;
796 void vdestream_close(VDESTREAM *vdestream)
798 free(vdestream);