vtun/lib.c

371 lines
8.2 KiB
C

/*
VTun - Virtual Tunnel over TCP/IP network.
Copyright (C) 1998-2008 Maxim Krasnyansky <max_mk@yahoo.com>
VTun has been derived from VPPP package by Maxim Krasnyansky.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
*/
/*
* $Id: lib.c,v 1.9.2.4 2013/07/07 20:21:20 mtbishop Exp $
*/
#include "config.h"
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <syslog.h>
#include <errno.h>
#include "vtun.h"
#include "linkfd.h"
#include "lib.h"
volatile sig_atomic_t __io_canceled = 0;
#ifndef HAVE_SETPROC_TITLE
/* Functions to manipulate with program title */
extern char **environ;
static char *title_start; /* start of the proc title space */
static char *title_end; /* end of the proc title space */
static int title_size;
void init_title(int argc,char *argv[], char *envp[], char *name)
{
int i;
/*
* Move the environment so settitle can use the space at
* the top of memory.
*/
for (i = 0; envp[i]; i++);
environ = (char **) malloc(sizeof (char *) * (i + 1));
for(i = 0; envp[i]; i++)
environ[i] = strdup(envp[i]);
environ[i] = NULL;
/*
* Save start and extent of argv for set_title.
*/
title_start = argv[0];
/*
* Determine how much space we can use for set_title.
* Use all contiguous argv and envp pointers starting at argv[0]
*/
for(i=0; i<argc; i++)
if( !i || title_end == argv[i])
title_end = argv[i] + strlen(argv[i]) + 1;
for(i=0; envp[i]; i++)
if( title_end == envp[i] )
title_end = envp[i] + strlen(envp[i]) + 1;
strcpy(title_start, name);
title_start += strlen(name);
title_size = title_end - title_start;
}
void set_title(const char *fmt, ...)
{
char buf[255];
va_list ap;
memset(title_start,0,title_size);
/* print the argument string */
va_start(ap, fmt);
vsnprintf(buf, sizeof buf, fmt, ap);
va_end(ap);
if( strlen(buf) > title_size - 1)
buf[title_size - 1] = '\0';
strcat(title_start, buf);
}
#endif /* HAVE_SETPROC_TITLE */
/*
* Print padded messages.
* Used by 'auth' function to force all messages
* to be the same len.
*/
int print_p(int fd,const char *fmt, ...)
{
char buf[VTUN_MESG_SIZE];
va_list ap;
memset(buf,0,sizeof(buf));
/* print the argument string */
va_start(ap, fmt);
if (vsnprintf(buf,sizeof(buf)-1, fmt, ap) >= VTUN_MESG_SIZE)
abort();
va_end(ap);
return write_n(fd, buf, sizeof(buf));
}
/* Read N bytes with timeout */
int readn_t(int fd, void *buf, size_t count, time_t timeout)
{
fd_set fdset;
struct timeval tv;
tv.tv_usec=0; tv.tv_sec=timeout;
FD_ZERO(&fdset);
FD_SET(fd,&fdset);
if( select(fd+1,&fdset,NULL,NULL,&tv) <= 0)
return -1;
return read_n(fd, buf, count);
}
/*
* Substitutes opt in place off '%X'.
* Returns new string.
*/
static char * subst_opt(char *str, struct vtun_sopt *opt)
{
register int slen, olen, sp, np;
register char *optr, *nstr, *tmp;
char buf[10];
if( !str ) return NULL;
slen = strlen(str) + 1;
if( !(nstr = malloc(slen)) )
return str;
sp = np = 0;
while( str[sp] ){
switch( str[sp] ){
case '%':
optr = NULL;
/* Check supported opt */
switch( str[sp+1] ){
case '%':
case 'd':
optr=opt->dev;
break;
case 'A':
optr=opt->laddr;
break;
case 'P':
snprintf(buf, sizeof buf, "%d",opt->lport);
optr=buf;
break;
case 'a':
optr=opt->raddr;
break;
case 'p':
snprintf(buf, sizeof buf, "%d",opt->rport);
optr=buf;
break;
case 'h':
optr=opt->host;
break;
default:
sp++;
continue;
}
if( optr ){
/* Opt found substitute */
olen = strlen(optr);
slen = slen - 2 + olen;
if( !(tmp = realloc(nstr, slen)) ){
free(nstr);
return str;
}
nstr = tmp;
memcpy(nstr + np, optr, olen);
np += olen;
}
sp += 2;
continue;
case '\\':
nstr[np++] = str[sp++];
if( !str[sp] )
continue;
/* fall through */
default:
nstr[np++] = str[sp++];
break;
}
}
nstr[np] = '\0';
return nstr;
}
/*
* Split arguments string.
* ' ' - group arguments
* Modifies original string.
*/
static void split_args(char *str, char **argv)
{
register int i = 0;
int mode = 0;
while( str && *str ){
switch( *str ){
case ' ':
if( mode == 1 ){
*str = '\0';
mode = 0;
i++;
}
break;
case '\'':
if( !mode ){
argv[i] = str+1;
mode = 2;
} else {
memmove(argv[i]+1, argv[i], str - argv[i]);
argv[i]++;
if( mode == 1 )
mode = 2;
else
mode = 1;
}
break;
case '\\':
if( mode ){
memmove(argv[i]+1, argv[i], str - argv[i]);
argv[i]++;
}
if( !*(++str) ) continue;
/*Fall through */
default:
if( !mode ){
argv[i] = str;
mode = 1;
}
break;
}
str++;
}
if( mode == 1 || mode == 2)
i++;
argv[i]=NULL;
}
#ifdef HAVE_WORKING_FORK
int run_cmd(void *d, void *opt)
{
struct vtun_cmd *cmd = d;
char *argv[50], *args;
int pid, st;
switch( (pid=fork()) ){
case 0:
break;
case -1:
vtun_syslog(LOG_ERR,"Couldn't fork()");
return 0;
default:
if( cmd->flags & VTUN_CMD_WAIT ){
/* Wait for termination */
if( waitpid(pid,&st,0) > 0 && (WIFEXITED(st) && WEXITSTATUS(st)) )
vtun_syslog(LOG_INFO,"Command [%s %.20s] error %d",
cmd->prog ? cmd->prog : "sh",
cmd->args ? cmd->args : "",
WEXITSTATUS(st) );
}
if( cmd->flags & VTUN_CMD_DELAY ){
struct timespec tm = { VTUN_DELAY_SEC, 0 };
/* Small delay hack to sleep after pppd start.
* Until I have no good solution for solving
* PPP + route problem */
nanosleep(&tm, NULL);
}
return 0;
}
args = subst_opt(cmd->args, opt);
if( !cmd->prog ){
/* Run using shell */
cmd->prog = "/bin/sh";
argv[0] = "sh";
argv[1] = "-c";
argv[2] = args;
argv[3] = NULL;
} else {
argv[0] = cmd->prog;
split_args(args, argv + 1);
}
execv(cmd->prog, argv);
vtun_syslog(LOG_ERR,"Couldn't exec program %s", cmd->prog);
exit(1);
}
#endif
void free_sopt( struct vtun_sopt *opt )
{
if( opt->dev ){
free(opt->dev);
opt->dev = NULL;
}
if( opt->laddr ){
free(opt->laddr);
opt->laddr = NULL;
}
if( opt->raddr ){
free(opt->raddr);
opt->raddr = NULL;
}
}
void vtun_syslog (int priority, char *format, ...)
{
static volatile sig_atomic_t in_syslog= 0;
char buf[255];
va_list ap;
if(! in_syslog) {
in_syslog = 1;
va_start(ap, format);
vsnprintf(buf, sizeof(buf)-1, format, ap);
syslog(priority, "%s", buf);
closelog();
va_end(ap);
in_syslog = 0;
}
}