/*
* libvdeplug - A library to connect to a VDE Switch.
* Copyright (C) 2006 Renzo Davoli, University of Bologna
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation version 2.1 of the License, or (at
* your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "libvdeplug.h"
#include <errno.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <pwd.h>
#include <grp.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#ifndef VDESTDSOCK
#define VDESTDSOCK "/var/run/vde.ctl"
#define VDETMPSOCK "/tmp/vde.ctl"
#endif
struct vdeconn {
int fdctl;
int fddata;
struct sockaddr_un inpath;
};
#define SWITCH_MAGIC 0xfeedface
#define MAXDESCR 128
enum request_type { REQ_NEW_CONTROL };
struct request_v3 {
uint32_t magic;
uint32_t version;
enum request_type type;
struct sockaddr_un sock;
char description[MAXDESCR];
};
VDECONN *vde_open_real(char *sockname,char *descr,int interface_version,
struct vde_open_args *open_args)
{
struct vdeconn *conn;
struct passwd *callerpwd;
struct request_v3 req;
int pid = getpid();
static struct sockaddr_un sockun;
static struct sockaddr_un dataout;
int port=0;
char *group=NULL;
int sockno=0;
int res;
mode_t mode=0700;
if (open_args != NULL) {
if (interface_version == 1) {
port=open_args->port;
group=open_args->group;
mode=open_args->mode;
}
else {
errno=EINVAL;
return NULL;
}
}
if ((conn=calloc(1,sizeof(struct vdeconn)))==NULL)
{
errno=ENOMEM;
return NULL;
}
//get the login name
callerpwd=getpwuid(getuid());
if((conn->fddata = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0){
int err=errno;
free(conn);
errno=err;
return NULL;
}
if((conn->fdctl = socket(AF_UNIX, SOCK_STREAM, 0)) < 0){
int err=errno;
close(conn->fddata);
free(conn);
errno=err;
return NULL;
}
if (sockname == NULL)
sockname=VDESTDSOCK;
else {
char *split;
if(sockname[strlen(sockname)-1] == ']' && (split=rindex(sockname,'[')) != NULL) {
*split=0;
split++;
port=atoi(split);
if (*sockname==0) sockname=VDESTDSOCK;
}
}
sockun.sun_family = AF_UNIX;
snprintf(sockun.sun_path, sizeof(sockun.sun_path), "%s/ctl", sockname);
if(connect(conn->fdctl, (struct sockaddr *) &sockun, sizeof(sockun))){
if (sockname == VDESTDSOCK) {
sockname=VDETMPSOCK;
snprintf(sockun.sun_path, sizeof(sockun.sun_path), "%s/ctl", sockname);
if(connect(conn->fdctl, (struct sockaddr *) &sockun, sizeof(sockun))){
snprintf(sockun.sun_path, sizeof(sockun.sun_path), "%s", sockname);
if(connect(conn->fdctl, (struct sockaddr *) &sockun, sizeof(sockun))){
close(conn->fddata);
close(conn->fdctl);
free(conn);
errno=ENOENT;
return NULL;
}
}
}
}
req.magic=SWITCH_MAGIC;
req.version=3;
req.type=REQ_NEW_CONTROL+(port << 8);
req.sock.sun_family=AF_UNIX;
/* First choice, store the return socket from the switch in the control dir*/
memset(req.sock.sun_path, 0, sizeof(req.sock.sun_path));
do
{
sprintf(req.sock.sun_path, "%s.%05d-%05d", sockname, pid, sockno++);
res=bind(conn->fddata, (struct sockaddr *) &req.sock, sizeof (req.sock));
}
while (res < 0 && errno == EADDRINUSE);
if (res < 0){
/* if it is not possible -> /tmp */
memset(req.sock.sun_path, 0, sizeof(req.sock.sun_path));
do
{
sprintf(req.sock.sun_path, "/tmp/vde.%05d-%05d", pid, sockno++);
res=bind(conn->fddata, (struct sockaddr *) &req.sock, sizeof (req.sock));
}
while (res < 0 && errno == EADDRINUSE);
if (res < 0){
int err=errno;
close(conn->fddata);
close(conn->fdctl);
free(conn);
errno=err;
return NULL;
}
}
memcpy(&(conn->inpath),&req.sock,sizeof(req.sock));
if (group) {
struct group *gs;
gid_t gid;
if ((gs=getgrnam(group)) == NULL)
gid=atoi(group);
else
gid=gs->gr_gid;
chown(conn->inpath.sun_path,-1,gid);
}
if (mode>=0)
chmod(conn->inpath.sun_path,mode);
snprintf(req.description,MAXDESCR,"%s user=%s PID=%d %s SOCK=%s",
descr,callerpwd->pw_name,pid,getenv("SSH_CLIENT")?getenv("SSH_CLIENT"):"",req.sock.sun_path);
if (send(conn->fdctl,&req,sizeof(req)-MAXDESCR+strlen(req.description),0) < 0) {
int err=errno;
close(conn->fddata);
close(conn->fdctl);
free(conn);
errno=err;
return NULL;
}
if (recv(conn->fdctl,&(dataout),sizeof(struct sockaddr_un),0)<0) {
int err=errno;
close(conn->fddata);
close(conn->fdctl);
free(conn);
errno=err;
return NULL;
}
if (connect(conn->fddata,(struct sockaddr *)&(dataout),sizeof(struct sockaddr_un))<0) {
int err=errno;
close(conn->fddata);
close(conn->fdctl);
free(conn);
errno=err;
return NULL;
}
chmod(dataout.sun_path,mode);
return conn;
}
ssize_t vde_recv(VDECONN *conn,char *buf,size_t len,int flags)
{
if (__builtin_expect(conn!=0,1))
return recv(conn->fddata,buf,len,0);
else {
errno=EBADF;
return -1;
}
}
ssize_t vde_send(VDECONN *conn,const char *buf,size_t len,int flags)
{
if (__builtin_expect(conn!=0,1))
return send(conn->fddata,buf,len,0);
else {
errno=EBADF;
return -1;
}
}
int vde_datafd(VDECONN *conn)
{
if (__builtin_expect(conn!=0,1))
return conn->fddata;
else {
errno=EBADF;
return -1;
}
}
int vde_ctlfd(VDECONN *conn)
{
if (__builtin_expect(conn!=0,1))
return conn->fdctl;
else {
errno=EBADF;
return -1;
}
}
int vde_close(VDECONN *conn)
{
if (__builtin_expect(conn!=0,1)) {
unlink(conn->inpath.sun_path);
close(conn->fddata);
close(conn->fdctl);
free(conn);
return 0;
} else {
errno=EBADF;
return -1;
}
}
syntax highlighted by Code2HTML, v. 0.9.1