911 lines
27 KiB
C
911 lines
27 KiB
C
/* $Id: eftp.c,v 1.4 1999/10/05 21:23:22 he Exp $ */
|
|
/*
|
|
Copyright 1997 by Henner Eisen
|
|
|
|
This code is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
This code 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
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public
|
|
License along with this library; if not, write to the Free
|
|
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
*/
|
|
|
|
/*
|
|
CAUTION: this description is somewhat outdated.
|
|
|
|
|
|
This is an experimental and incomplete program. The purpose of
|
|
this program is NOT to provide a perfect eft (EUROFILE transfer) client
|
|
for isdn4linux. It's more intended to be a demonstration
|
|
|
|
- to show, how X.25 sockets can be used to connect to a remote computer
|
|
using the ISO-8208 (X.25 DTE-DTE) protocol on top of isdn4linux.
|
|
- to test data transfer on top of x.25 on top of isdn4linux
|
|
if your isdn peer can run an eft server.
|
|
- to give some insights to the EUROFILE protocol (there is no public
|
|
documentation available anywhere else).
|
|
|
|
The whole eft protocol is implemented inside a library, usable via the
|
|
interface defined in eft.h. My intention is to continue developing
|
|
and maintaining the core eft protocol implementation. Based on the library,
|
|
uers are welcome to write other eft clients (and servers).
|
|
(Think, i.e., about a kde version, http-to-eft gateway, or just a more
|
|
sophisticated command line interface than this one).
|
|
|
|
This program does not close low layer isdn connections by itsself. PLEASE
|
|
monitor the isdn connection when you are using this program (this is
|
|
because the current linux x.25 was initially nor intended for DTE-DTE
|
|
mode on top of isdn, thus, it doesn't close the link after the x.25
|
|
connections are cleared). If the connection isn't cleared by your peer
|
|
after the end of your eft session, use the "isdnctrl hangup" command to
|
|
do this manually. Or use a script wrapper (such as eftp.sh).
|
|
|
|
And security was not a primary issue when writing this program (i.e.
|
|
buffer overflow checks might be missing where necessary). Thus, a bad guy
|
|
might exploit all kinds of bugs in order to break into your system.
|
|
|
|
As this program is not tested well, please ask the operator of
|
|
your remote eft server before trying to connect to it.
|
|
Until now, this client hasn't caused any harm, but as testing was very
|
|
limited, I cannot guaranty this.
|
|
|
|
|
|
Now, if you want to test it, do the following:
|
|
|
|
|
|
Set up an outgoing isdn network interface, configured for encap x25iface,
|
|
l2_prot x75i and your eft serving peer's remote phone number.
|
|
|
|
Create an appropriate x25 route to this interface.
|
|
|
|
(You can use the "ix25test" script to do all of that).
|
|
|
|
Assuming your user name on the eft server is "myname" and your password is
|
|
"mypass" and the X.25 address that is routed to your isdn interface is "05".
|
|
Type
|
|
|
|
eftp -x 05 myname/mypass
|
|
|
|
from the shell's prompt.
|
|
|
|
This will try to connect to the remote eft server. A lot of debugging
|
|
output (protocol trace) will appear on your terminal while this is going on.
|
|
|
|
If the connection was successful you can type
|
|
|
|
dir *
|
|
|
|
from the "eftp>" prompt. You should get a listing of the server's directory.
|
|
Assuming the directory contains a file "TEST.GIF" type
|
|
|
|
get TEST.GIF
|
|
|
|
to download that file to your computer. The transfer header of the file
|
|
received is not evaluated, just skipped. The contents of the transferred
|
|
file is written to your disk as received (i.e. neither owner nor
|
|
permissions of the file are retreived from the transfer header, no code
|
|
conversion for text files is performed, ...).
|
|
|
|
Do submit a file to the remote computer type
|
|
|
|
put FILE
|
|
|
|
Furthermore, there are two other commands, namely "quit" and "help".
|
|
|
|
Good luck.
|
|
|
|
*/
|
|
|
|
/* for strsignal() */
|
|
#define _GNU_SOURCE
|
|
|
|
#include <stdio.h>
|
|
#include <termios.h>
|
|
#include <unistd.h>
|
|
#include <sys/time.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <signal.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/wait.h>
|
|
#include <linux/x25.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <glob.h>
|
|
#include <fnmatch.h>
|
|
#include <ctype.h>
|
|
|
|
#include <tdu_user.h>
|
|
#include <eft.h>
|
|
|
|
#define MAX_COMMAND_LINE_LEN 4096
|
|
|
|
/* is in net/x25.h, not in the public header file linux/x25.h. Why?*/
|
|
#ifndef X25_MAX_CUD_LEN
|
|
#define X25_MAX_CUD_LEN 128
|
|
#endif
|
|
|
|
|
|
#ifdef CONFIG_EFTP_READLINE
|
|
|
|
#include <readline/readline.h>
|
|
#include <readline/history.h>
|
|
|
|
#endif
|
|
|
|
/*
|
|
If multi_prompt is 0, the mget and mput commands prompt for each matching
|
|
filename. Otherwise, they just transmit all matching files.
|
|
*/
|
|
static int multi_prompt = 0;
|
|
|
|
/*
|
|
Prompts for "y" or "n" if multi_prompt != 0,
|
|
returns 1 for "y" or 0 for "n".
|
|
If stdin is not a TTY, it returns 1 always.
|
|
*/
|
|
static int multi_prompt_yes(char* cmdname, char* filename)
|
|
{
|
|
int c,r;
|
|
if( !multi_prompt || !isatty(STDIN_FILENO)) return 1;
|
|
while(1) {
|
|
fprintf(stderr,"%s %s (y/n)? ",cmdname,filename);
|
|
fflush(stderr);
|
|
r = c = tolower(getchar());
|
|
while(c!='\n' && c!=EOF)
|
|
c = getchar();
|
|
// fprintf(stderr,"\n");
|
|
if('y' == r) return 1;
|
|
if('n' == r) return 0;
|
|
}
|
|
/* not reached */
|
|
}
|
|
|
|
/*
|
|
* A simple parser for the eftp command line
|
|
*/
|
|
/*
|
|
* FIXME: return values of local commands (!, lcd, lpwd, ldir)
|
|
*/
|
|
int process_cmd_line (struct eft *eft, unsigned char *buf)
|
|
{
|
|
static int mget_case_ignore = 0;
|
|
char **pp = (char **) & buf, *c, *arg1, *arg2, *obuf=buf, *cmd;
|
|
|
|
int ret = 1, count, r;
|
|
|
|
if( *buf == '!' ){
|
|
r = system(buf+1);
|
|
if( (r<0) || (r==127) ){
|
|
perror("system()");
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
c = strsep(pp," \t\n");
|
|
arg1 = strsep(pp," \t\n");
|
|
arg2 = strsep(pp," \t\n");
|
|
if( arg2 == 0 || *arg2 == 0) arg2 = arg1;
|
|
|
|
if( c == NULL ) return -1;
|
|
if( strcmp(c, "dir") == 0 ){
|
|
if( (arg1 == NULL) || (*arg1 == 0) ) arg1 = "*";
|
|
ret = eft_dir_fd ( eft, 1, arg1, 0 );
|
|
} else if( strcmp(c, "xdir") == 0 ){
|
|
if( (arg1 == NULL) || (*arg1 == 0) ) arg1 = "*";
|
|
ret = eft_xdir_txt ( eft, arg1 );
|
|
} else if( strcmp(c, "msg") == 0 ){
|
|
if( (arg1 == NULL) || (*arg1 == 0) ) {
|
|
eft_prompt("EFT-Message: ");
|
|
count = read( STDIN_FILENO, obuf, 254);
|
|
if ( count < 0 ) {
|
|
perror("while reading msg line");
|
|
} else {
|
|
obuf[count] = 0;
|
|
ret = eft_msg ( eft, obuf );
|
|
}
|
|
} else {
|
|
ret = eft_msg ( eft, arg1 );
|
|
}
|
|
} else if( strcmp(c, "get") == 0 ) {
|
|
if( (arg1 == NULL) || (*arg1 == 0) ){
|
|
fprintf(stderr,"eftp get: file name is missing\n");
|
|
return 0;
|
|
}
|
|
ret = eft_load( eft, arg2, arg1 );
|
|
} else if( strcmp(c, "put") == 0 ) {
|
|
if( (arg1 == NULL) || (*arg1 == 0) ){
|
|
fprintf(stderr,"eftp put: file name is missing\n");
|
|
return 0;
|
|
}
|
|
ret = eft_save( eft, arg2, arg1 );
|
|
|
|
/* Is this necessary as the same functionality is
|
|
* provided by !ls ? Maybe, if we could provide
|
|
* the local output in the same format as dir/xdir
|
|
*/
|
|
} else if( (strcmp(c, "ldir") == 0) ||
|
|
(strcmp(c, "lls") == 0) ) {
|
|
ret = 0; /* Local problems should not cause a disconnect */
|
|
if(! arg1) arg1="";
|
|
cmd = (char*)malloc(strlen(c)+strlen(arg1)+2);
|
|
if( cmd ){
|
|
sprintf(cmd,"%s %s",c+1,arg1);
|
|
r = system(cmd);
|
|
if( (r<0) || (r==127) ){
|
|
perror("system()");
|
|
}
|
|
free(cmd);
|
|
}
|
|
} else if( strcmp(c, "lcd") == 0 ) {
|
|
char* cwd = NULL;
|
|
ret = 0;
|
|
if(arg1 && *arg1) {
|
|
if( strcmp(arg1,"~") == 0 ){
|
|
r = chdir(getenv("HOME"));
|
|
} else {
|
|
r = chdir(arg1);
|
|
}
|
|
if(r<0){
|
|
perror("chdir()");
|
|
}
|
|
}
|
|
cwd = getcwd(NULL,0);
|
|
printf("Local directory now %s\n",cwd);
|
|
} else if( strcmp(c, "mput") == 0 ) {
|
|
glob_t glob_result;
|
|
int r;
|
|
size_t i;
|
|
char* p = arg1;
|
|
if( (arg1 == NULL) || (*arg1 == 0) ) {
|
|
fprintf(stderr,"eftp mput: file name pattern is missing\n");
|
|
return 0;
|
|
}
|
|
while(p) {
|
|
r = glob(p,0,NULL,&glob_result);
|
|
for(i=0; i<glob_result.gl_pathc; i++) {
|
|
if( multi_prompt_yes("mput", glob_result.gl_pathv[i]) ) {
|
|
ret = eft_save( eft, glob_result.gl_pathv[i],
|
|
glob_result.gl_pathv[i] );
|
|
if(ret) return ret; /* or should we ignore it? */
|
|
}
|
|
}
|
|
if(arg1 == arg2) /* only one argument */
|
|
p = NULL;
|
|
else if(p == arg1) /* at least two arguments */
|
|
p = arg2;
|
|
else p = strsep(pp," \t\n"); /* more arguments */
|
|
}
|
|
return 0;
|
|
} else if( strcmp(c, "mget") == 0 ) {
|
|
char* p = arg1;
|
|
if( (arg1 == NULL) || (*arg1 == 0) ) {
|
|
fprintf(stderr,"eftp mget: file name pattern is missing\n");
|
|
return 0;
|
|
}
|
|
while(p) {
|
|
FILE* f = tmpfile();
|
|
int tmp_fd = fileno(f);
|
|
char s[1024];
|
|
ret = eft_dir_fd ( eft, tmp_fd, p, 0 );
|
|
fflush(f);
|
|
fseek(f,0,SEEK_SET);
|
|
while(fgets(s,sizeof(s),f))
|
|
{
|
|
char* fname = strtok(s," \t\n");
|
|
|
|
/*
|
|
* glibc2 (at least in default environment) seems not to know
|
|
* about FNM_CASEFOLD
|
|
* FIXME: should this be removed or repleced by something else?
|
|
*
|
|
* For now, avoid glibc2 problems by
|
|
*/
|
|
#ifndef FNM_CASEFOLD
|
|
#define FNM_CASEFOLD 0
|
|
#endif
|
|
|
|
if( 0 == fnmatch(p, fname,
|
|
mget_case_ignore? FNM_CASEFOLD:0) ){
|
|
if( multi_prompt_yes("mget",fname) ) {
|
|
ret = eft_load( eft, fname, fname );
|
|
if(ret) return ret; /* or should we ignore it? */
|
|
}
|
|
}
|
|
}
|
|
fclose(f);
|
|
if(arg1 == arg2) /* only one argument */
|
|
p = NULL;
|
|
else if(p == arg1) /* at least two arguments */
|
|
p = arg2;
|
|
else p = strsep(pp," \t\n"); /* more arguments */
|
|
}
|
|
return 0;
|
|
} else if( strcmp(c, "prompt") == 0 ) {
|
|
if( (arg1 == NULL) || (*arg1 == 0) )
|
|
multi_prompt = !multi_prompt;
|
|
else if( strcmp(arg1,"on") == 0)
|
|
multi_prompt = 1;
|
|
else if( strcmp(arg1,"off") == 0)
|
|
multi_prompt = 0;
|
|
else {
|
|
fprintf(stderr,"eftp prompt: need \"on\" or \"off\" or no argument\n");
|
|
return 0;
|
|
}
|
|
fprintf(stderr,"Interactive mode %s.\n", multi_prompt? "on":"off");
|
|
ret = 0;
|
|
} else if( strcmp(c, "case") == 0 ) {
|
|
if( (arg1 == NULL) || (*arg1 == 0) )
|
|
mget_case_ignore = !mget_case_ignore;
|
|
else if( strcmp(arg1,"on") == 0)
|
|
mget_case_ignore = 1;
|
|
else if( strcmp(arg1,"off") == 0)
|
|
mget_case_ignore = 0;
|
|
else {
|
|
fprintf(stderr,"eftp case: need \"on\" or \"off\" or no argument\n");
|
|
return 0;
|
|
}
|
|
fprintf(stderr,"Case mapping %s.\n", mget_case_ignore? "on":"off");
|
|
ret = 0;
|
|
} else if( strcmp(c, "quit") == 0 ||
|
|
strcmp(c, "close") == 0 ||
|
|
strcmp(c, "stop") == 0 ||
|
|
strcmp(c, "end") == 0 ||
|
|
strcmp(c, "exit") == 0 ||
|
|
strcmp(c, "bye") == 0 ) {
|
|
return -1;
|
|
} else if( strcmp(c, "help") == 0 || strcmp(c,"?") == 0 ) {
|
|
fprintf(stderr, "known commands:\n"
|
|
"get FILE [LOCAL_NAME] get file from remote\n"
|
|
"dir PATTERN get directory from remote\n"
|
|
"xdir PATTERN get directory with extended information from remote\n"
|
|
"put FILE [REMOTE_NAME] put file to remote\n"
|
|
"list list all directories\n"
|
|
"slist list all subdirectories\n"
|
|
"cd [DIR] change working directory\n"
|
|
"mkdir DIR make remote directory\n"
|
|
"pwd print working directory\n"
|
|
"msg MESSAGE submit a printable message\n"
|
|
"lcd [DIR] change local directory\n"
|
|
"lls list local directory\n"
|
|
"! COMMAND execute shell command\n"
|
|
"mput PATTERN put multiple files to remote\n"
|
|
"mget PATTERN get multiple files from remote\n"
|
|
"prompt [on|off] set or toggle interactive prompting for mget and mput\n"
|
|
"case [on|off] set or toggle mget case sensitivity\n"
|
|
"quit close connections and quit\n"
|
|
"(mkdir and list are not supported by most servers, many even don't\n"
|
|
" support xdir, slist, cd, pwd, nor msg)\n"
|
|
);
|
|
} else if( strcmp(c, "list") == 0 ) {
|
|
ret = eft_list_fd( eft, 1, 0 );
|
|
} else if( strcmp(c, "slist") == 0 ) {
|
|
ret = eft_slist_fd( eft, 1, 0 );
|
|
} else if( strcmp(c, "cd") == 0 ) {
|
|
if( (arg1 == NULL) || (*arg1 == 0) ) arg1 = NULL;
|
|
ret = eft_cd( eft, arg1 );
|
|
} else if( strcmp(c, "pwd") == 0 ) {
|
|
char dir[EFT_MAX_FSTORE_LEN+1];
|
|
ret = eft_getcwd( eft, dir );
|
|
if(ret>=0) printf("Current filestore is %s\n",dir);
|
|
} else if( strcmp(c, "mkdir") == 0 ) {
|
|
if( (arg1 == NULL) || (*arg1 == 0) ){
|
|
fprintf(stderr,"eftp mkdir: dir name is missing\n");
|
|
return 0;
|
|
}
|
|
ret = eft_mkdir( eft, arg1 );
|
|
} else {
|
|
fprintf( stderr, "unknown command, type \"?\" or \"help\"\n");
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
static struct eft *eft;
|
|
|
|
#ifdef CONFIG_EFTP_READLINE
|
|
static int disconnect_please = 0;
|
|
|
|
static void readline_callback_handler(void)
|
|
{
|
|
static char* line_read = NULL;
|
|
int r;
|
|
|
|
/* If the buffer has already been allocated, return the memory
|
|
to the free pool. */
|
|
if( line_read ) {
|
|
free(line_read);
|
|
line_read = (char *)NULL;
|
|
}
|
|
if( *rl_line_buffer ) {
|
|
line_read = strdup(rl_line_buffer);
|
|
add_history(line_read);
|
|
/*
|
|
* XXX: caution: if the cmd is "msg", process_cmd_line might prompt and
|
|
* read() from stdin now from this callback, which has caused problems.
|
|
* Probably, readline() is not designed for this.
|
|
* This needs further investigation (and a fix :-).
|
|
*
|
|
* (Not a showstopper, as the msg cmd doesn't serve anything. It's just there
|
|
* for completeness and the problem is not triggered when the message is
|
|
* provided by means of the msg cmd parameter)
|
|
*/
|
|
if( (r=process_cmd_line(eft, line_read)) < 0 ){
|
|
if( r < -1) fprintf(stderr,"cmd execution error %d<0\n",r);
|
|
disconnect_please = 1;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void pr_usage(FILE * f, char *cmd)
|
|
{
|
|
fprintf(f,"usage: %s eftp [ -i ISDN_NO | -x X25_ADDRESS ]"
|
|
" [ -u USER[/PASSWORD] ] [-p] [-h]\n", cmd);
|
|
}
|
|
|
|
static void show_help(char *cmd)
|
|
{
|
|
pr_usage(stderr, cmd);
|
|
printf("\t-i\t isdn no of EUROFILE server\n"
|
|
"\t-x\t X.25 address of EUROFILE server\n"
|
|
"\t-u\t user name used to login on server. A password may\n"
|
|
"\t\t be appended, separated by a '/' character\n"
|
|
"\t-p\t inhibit prompting for a password\n"
|
|
"\t-h\t show help message\n"
|
|
);
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
/*
|
|
* CAUTION: The initial code part might execute suid root.
|
|
* See further remarks below if you change eftp.c
|
|
*/
|
|
struct sockaddr_x25 x25bind, x25connect;
|
|
struct x25_route_struct x25_route;
|
|
int s, count, on=1, selval, prompt_for_pw = 1;
|
|
unsigned char called[TDU_PLEN_ADDR+1], udata[TDU_PLEN_UDATA+1];
|
|
uid_t ruid, euid;
|
|
|
|
fd_set rfds;
|
|
|
|
unsigned char * ident;
|
|
char c, *isdn_no=NULL, *x25_no=NULL, *user=NULL,
|
|
*arg0 = *argv&&**argv ? *argv : "eftp",
|
|
*buf; /* Has to be dynamic memory for getdelim() */
|
|
|
|
extern char * optarg;
|
|
size_t buflen = MAX_COMMAND_LINE_LEN;
|
|
struct x25_calluserdata cud;
|
|
struct x25_facilities facilities;
|
|
pid_t pid;
|
|
struct linger ling = { 1 /* Linger active */,
|
|
500 /* wait up to 5 seconds on close */ };
|
|
sigset_t sig_pipe;
|
|
|
|
sigemptyset(&sig_pipe);
|
|
sigaddset(&sig_pipe, SIGPIPE);
|
|
|
|
buf = (char*)malloc(buflen);
|
|
|
|
printf("\nThis is ALPHA test software (incomplete, non-protocol-"
|
|
"conformant, buggy, etc).\n\n ABSOLUTELEY NO WARRENTY!\n\n"
|
|
"Copyright 1997 by Henner Eisen (eis@baty.hanse.de)\n"
|
|
"The GNU (Library) General Public License, Version 2, applies.\n\n\n");
|
|
|
|
if (argc < 1) {
|
|
pr_usage(stderr,arg0);
|
|
exit(1);
|
|
}
|
|
|
|
while ((c = getopt(argc, argv, "i:x:u:ph")) != EOF) {
|
|
switch (c) {
|
|
case 'u':
|
|
user = optarg;
|
|
break;
|
|
case 'i':
|
|
isdn_no = optarg;
|
|
break;
|
|
case 'x':
|
|
x25_no = optarg;
|
|
break;
|
|
case 'p':
|
|
prompt_for_pw=0;
|
|
break;
|
|
case 'h':
|
|
show_help(arg0);
|
|
exit(0);
|
|
}
|
|
}
|
|
|
|
ruid = getuid();
|
|
euid = geteuid();
|
|
if( isdn_no ){
|
|
/*
|
|
* If the destinatioin is given by means of an isdn number,
|
|
* we will try to dynamically create an isdn X.25 network
|
|
* interface and an x.25 route through it. In order to
|
|
* reliably hang up the connection later -- even if this
|
|
* programm crashes -- we will fork a child process in charge
|
|
* of the real work and ourselves will only wait for that
|
|
* child to exit such that we can clean up the low layer
|
|
* connection afterwards.
|
|
*
|
|
* In order to dynamically create isdn network interfaces
|
|
* and to set them up, we need certain priviliges (write
|
|
* access to /dev/isdnctrl and netadmin capability). However,
|
|
* we don't want to grant those priviliges to the executing
|
|
* eftp program. Forking also allows us to run the child
|
|
* process with fewer priviliges than ourselves.
|
|
*/
|
|
pid_t pid;
|
|
|
|
/*
|
|
* First we check if the user is allowed to open outgoing
|
|
* isdn connections. We assume that the user is allowed to
|
|
* do so if he has write permissions to /dev/ttyI0.
|
|
* (In that case, the user could also open outgoing
|
|
* connections by writing AT commands to /dev/ttyI0).
|
|
*/
|
|
#define EFTP_PERMISSION_CHECK_FILE "/dev/ttyI0"
|
|
if( access(EFTP_PERMISSION_CHECK_FILE,W_OK) ){
|
|
perror("eftp: User is not allowed to open outgoing "
|
|
"isdn connections: \n\t"
|
|
EFTP_PERMISSION_CHECK_FILE );
|
|
exit(1);
|
|
}
|
|
|
|
fprintf(stderr, "Setting up isdn x25 network interface\n");
|
|
if( eft_get_x25route(&x25connect,&x25_route,isdn_no) ){
|
|
fprintf(stderr,"eftp: unable to get an X.25 route for isdn number %s\n",isdn_no);
|
|
exit(1);
|
|
}
|
|
pid = fork();
|
|
if( pid<0 ){
|
|
perror("eftp: fork()");
|
|
exit(1);
|
|
} else if( pid > 0 ) {
|
|
/*
|
|
* parent process
|
|
*
|
|
* We first wait until the child no longer needs the
|
|
* x25 route and clear the route (needs netadmin
|
|
* capability)
|
|
*/
|
|
int status, err=0;
|
|
|
|
setreuid(euid,euid);
|
|
close(s);
|
|
eft_wait_release_route();
|
|
eft_release_route(&x25_route);
|
|
/*
|
|
* Finally, wait for the child to exit and clear
|
|
* the low layer isdn connection and remove
|
|
* dynamically created interfaces (needs netadmin
|
|
* capability and write access to /dev/isdnctrl)
|
|
*/
|
|
if( wait(&status) != pid ){
|
|
perror("eftp supervisor: wait failed");
|
|
err = 1;
|
|
} else {
|
|
if(WIFSIGNALED(status)){
|
|
tdu_printf(TDU_LOG_ERR,
|
|
"internal error in eftp[%d]: %s\n\tyou might try to debug eftp using gdb\n",
|
|
pid, strsignal(WTERMSIG(status)));
|
|
err = 2;
|
|
}
|
|
}
|
|
eft_dl_disconnect( x25_route.device );
|
|
eft_release_device( x25_route.device );
|
|
exit(err);
|
|
} else {
|
|
/* Child process
|
|
*
|
|
* Just contiunue processing the protocol.
|
|
*/
|
|
;
|
|
}
|
|
} else if( x25_no ){
|
|
strncpy(x25connect.sx25_addr.x25_addr, x25_no, 15);
|
|
} else {
|
|
fprintf(stderr, "Neither isdn nor X.25 address specified.\n"
|
|
" Assuming route to empty X.25 address\n");
|
|
strcpy(x25connect.sx25_addr.x25_addr, "");
|
|
}
|
|
/*
|
|
* Never remove this, otherwise a suid eftp might continue to
|
|
* run with full root priviliges!
|
|
*/
|
|
setreuid(ruid,ruid);
|
|
/*
|
|
* Further, any code before the above statement must be carefully
|
|
* written to resist any kind of suid attacks.
|
|
*
|
|
* Be aware of this if you change eftp.c! In particular, make shure
|
|
* that new/modified code does not allow for buffer overflow attacks
|
|
* by means of command line arguments or environment variables.
|
|
* The same holds for code of any function called from above.
|
|
*/
|
|
|
|
/* build ident string [uid/password] from various input sources */
|
|
ident = NULL;
|
|
if( user ) {
|
|
int ulen = strlen(user);
|
|
char * pos = strchr(user,'/');
|
|
if(pos){
|
|
prompt_for_pw = 0;
|
|
}
|
|
if( prompt_for_pw ){
|
|
char pwbuf[20];
|
|
struct termios saved, noecho;
|
|
fprintf(stderr,"password: ");
|
|
tcgetattr (STDIN_FILENO, &saved);
|
|
noecho = saved;
|
|
noecho.c_lflag &= ~ECHO;
|
|
tcsetattr (STDIN_FILENO, TCSANOW, &noecho);
|
|
if( fgets(pwbuf,20,stdin) ){
|
|
ident=malloc(strlen(pwbuf)+ulen+2);
|
|
if(ident){
|
|
strcpy(ident,user);
|
|
strcat(ident,"/");
|
|
strcat(ident,pwbuf);
|
|
/* remove traling new line left by fgets*/
|
|
ident[strlen(ident)-1]=0;
|
|
}
|
|
}
|
|
tcsetattr (STDIN_FILENO, TCSANOW, &saved);
|
|
} else {
|
|
ident = user;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* First section of this program establishes an X.25 DTE-DTE
|
|
* connection to the remote eft server using the socket interface.
|
|
* Several set ups have to be performed before.
|
|
*/
|
|
|
|
x25bind.sx25_family = AF_X25;
|
|
strcpy(x25bind.sx25_addr.x25_addr, "" );
|
|
|
|
x25connect.sx25_family = AF_X25;
|
|
|
|
s = socket(AF_X25, SOCK_SEQPACKET, 0);
|
|
if (s < 0) {
|
|
perror("eftp: socket creation failed");
|
|
fprintf(stderr,"\t(Maybe your kernel was not compiled with "
|
|
"X.25 PLP support enabled\n"
|
|
"\tor it was compiled as a module but the x25 "
|
|
"module is not loaded)\n");
|
|
return 1;
|
|
}
|
|
|
|
/* In order to recognize the lower layer SBV messages we need
|
|
to be aware of the Q-bit included with each X.25 packet,
|
|
*/
|
|
if (setsockopt(s, SOL_X25, X25_QBITINCL, &on, sizeof(on)) < 0 ) {
|
|
perror("eftp: setsockopt() failed");
|
|
return 1;
|
|
}
|
|
|
|
/* eft requires a packet size of (at least) 1024 bytes
|
|
*/
|
|
if( ioctl( s, SIOCX25GFACILITIES, &facilities ) != 0 ){
|
|
perror("eftp: SIOCX25GFACILITIES failed");
|
|
return 1;
|
|
}
|
|
printf("current facilies wi %d wo %d pi %d po %d\n",
|
|
facilities.winsize_in,
|
|
facilities.winsize_out,
|
|
facilities.pacsize_in,
|
|
facilities.pacsize_out);
|
|
|
|
facilities.winsize_in = 7;
|
|
/* avm server hangs with outgoing winsize=7 */
|
|
facilities.winsize_out = 6;
|
|
facilities.pacsize_in = X25_PS1024;
|
|
facilities.pacsize_out = X25_PS1024;
|
|
|
|
printf("requested facilies wi %d wo %d pi %d po %d\n",
|
|
facilities.winsize_in,
|
|
facilities.winsize_out,
|
|
facilities.pacsize_in,
|
|
facilities.pacsize_out);
|
|
|
|
/* Unfortunatly, this doesn't work with kernels up to at least 2.1.90
|
|
unless the socket is in the listen state (x.25 kernel bug)
|
|
unless af_x25.c is patched. */
|
|
printf("Trying to set X.25 facilities on socket ...\n");
|
|
if( ioctl( s, SIOCX25SFACILITIES, &facilities ) != 0 ){
|
|
perror("eftp: SIOCX25SFACILITIES failed");
|
|
return 1;
|
|
}
|
|
/*
|
|
* request of eft service is indicated to the server by a special
|
|
* direct call user data string in our x25 call request packet
|
|
*/
|
|
cud.cuddata[0] = 0;
|
|
cud.cuddata[1] = 0;
|
|
cud.cuddata[2] = 0;
|
|
cud.cuddata[3] = 0;
|
|
strncpy( cud.cuddata+4, "EUROSFT92", X25_MAX_CUD_LEN-4 );
|
|
cud.cudlength = 13;
|
|
if( ioctl( s, SIOCX25SCALLUSERDATA, &cud ) != 0 ){
|
|
perror("eftp: SIOCX25SCALLUSERDATA failed");
|
|
return 1;
|
|
}
|
|
|
|
/* connect to destination x25 address of socket: The address is not
|
|
needed by eft when operating over isdn in X.25-DTE-DTE mode. But as
|
|
we currently cannot set the ISDN number of our peer from this
|
|
programm we use x25 addresses to select different peers. The mapping
|
|
X25 address -> peer's ISDN number must be set up by assigning
|
|
the peer's ISDN number as the outgoing number to an isdn network
|
|
interface (using the isdnctrl utility) and by setting up an
|
|
X.25 route (using the x25route utility) that associates an
|
|
x25 address with that network interface.
|
|
*/
|
|
|
|
if (bind(s, (struct sockaddr *)&x25bind, sizeof (x25bind)) < 0) {
|
|
perror("eftp: unable to bind address to socket");
|
|
exit(1);
|
|
}
|
|
|
|
fprintf(stderr, "Trying to establish X.25 DTE-DTE connection to "
|
|
"x25 address \"%s\" ...\n", x25connect.sx25_addr.x25_addr);
|
|
if (connect(s, (struct sockaddr *)&x25connect, sizeof (x25connect)) < 0) {
|
|
perror("eftp: Unable to connect to remote host");
|
|
return 1;
|
|
}
|
|
fprintf(stderr,"eftp: X.25 connection established.\n");
|
|
|
|
if(isdn_no){
|
|
/* Now we are connected and don't need the X.25 route
|
|
* any longer. We tell our parent to release it. Thus,
|
|
* the route is free for re-use by other eftp clients.
|
|
* Relasing it also disables other users to play dirty
|
|
* tricks on us by piggybacking other X.25 connection
|
|
* throug our isdn connection.
|
|
*/
|
|
eft_signal_release_route(&x25_route);
|
|
}
|
|
|
|
if( ioctl( s, SIOCX25GFACILITIES, &facilities ) != 0 ){
|
|
perror("eftp: SIOCX25GFACILITIES failed");
|
|
return 1;
|
|
}
|
|
printf("active facilies wi %d wo %d pi %d po %d\n",
|
|
facilities.winsize_in,
|
|
facilities.winsize_out,
|
|
facilities.pacsize_in,
|
|
facilities.pacsize_out);
|
|
|
|
/*
|
|
* Now, the x.25 DTE-DTE connection is up. On top of that,
|
|
* the higer layer eft connection needs to be established.
|
|
* There are several higer layers, but this is taken care of
|
|
* be the eft_connect() function.
|
|
*/
|
|
|
|
eft = eft_make_instance();
|
|
|
|
/* This specifies the amount of (debugging) output printed to stderr*/
|
|
/* tdu_stderr_mask = TDU_LOG_FH | TDU_LOG_REW | TDU_LOG_ERR; */
|
|
tdu_stderr_mask = TDU_LOG_ERR | TDU_LOG_IER | TDU_LOG_OER /* | TDU_LOG_DBG
|
|
| TDU_LOG_HASH | TDU_LOG_TMP */ ;
|
|
#if 0
|
|
/* for maximum amount of debugging output use */
|
|
tdu_stderr_mask = -1 /* ^ TDU_LOG_TMP ^ TDU_LOG_TRC */;
|
|
/* */
|
|
#endif
|
|
/* Attach the connected x.25 socket to the eft protocol state
|
|
* machine */
|
|
eft_attach_socket(eft,s);
|
|
/*
|
|
* block SIGPIPE such that peer initiated disconnects will
|
|
* result in write error indications
|
|
*/
|
|
if( sigprocmask(SIG_BLOCK, &sig_pipe, NULL) )
|
|
perror("sigprocmask()");
|
|
#if 1
|
|
setsockopt(s,SOL_SOCKET,SO_LINGER,&ling,sizeof(ling));
|
|
#endif
|
|
/* and finally establish logical eft connection */
|
|
if( eft_connect( eft, ident) < 0 ) {
|
|
fprintf(stderr, "eftp: connection failed\n");
|
|
goto Ende;
|
|
}
|
|
|
|
fprintf(stderr,"eftp: logged in");
|
|
if(eft_printable_called_addr(eft,called) > 0)
|
|
fprintf(stderr," at %s",called);
|
|
if(eft_printable_assoc_udata(eft,udata))
|
|
fprintf(stderr,", udata=%s",udata);
|
|
fprintf(stderr,"\n");
|
|
|
|
/*
|
|
* Now the main loop processing commands from stdin
|
|
*/
|
|
#ifdef CONFIG_EFTP_READLINE
|
|
|
|
rl_readline_name = "eftp";
|
|
rl_inhibit_completion = 1;
|
|
|
|
if(isatty(STDIN_FILENO)) {
|
|
rl_callback_handler_install("eftp> ",readline_callback_handler);
|
|
while ( !disconnect_please && eft_is_up(eft) ) {
|
|
FD_ZERO(&rfds);
|
|
FD_SET(STDIN_FILENO, &rfds);
|
|
selval = eft_select(eft, STDIN_FILENO+1,
|
|
&rfds, NULL, NULL, NULL);
|
|
if( selval < 0 ) {
|
|
disconnect_please = 1;
|
|
} else {
|
|
rl_callback_read_char();
|
|
}
|
|
}
|
|
rl_callback_handler_remove();
|
|
} else /* use the old style loop if stdin is not a tty */
|
|
#endif
|
|
{
|
|
while ( eft_is_up(eft) ) {
|
|
FD_ZERO(&rfds);
|
|
FD_SET(STDIN_FILENO, &rfds);
|
|
eft_prompt("eftp> ");
|
|
selval = eft_select(eft, STDIN_FILENO+1,
|
|
&rfds, NULL, NULL, NULL);
|
|
fprintf(stderr,"aft_sel\n");
|
|
if( selval < 0 ) goto disconnect;
|
|
#ifdef HAVE_GETDELIM
|
|
/* XXX replace this by a non-blocking version
|
|
getline()/getdelim() is a GNU extension:
|
|
it reads whole lines like fgets(), but it's safer.
|
|
*/
|
|
count = getdelim( &buf, &buflen, '\n' , stdin );
|
|
#else
|
|
/* In case you don't have getdelim(). */
|
|
count = fgets( buf, buflen, stdin )? strlen(buf) : -1;
|
|
#endif
|
|
if ( count < 0 ) {
|
|
perror("while reading cmd line, count<0");
|
|
goto disconnect;
|
|
} else {
|
|
int r;
|
|
if( (r=process_cmd_line(eft, buf)) < 0 ){
|
|
if(r < -1) fprintf(stderr,"cmd execution error %d<0\n",r);
|
|
goto disconnect;
|
|
}
|
|
}
|
|
}
|
|
/* ??? fclose(my_stdin) ??? */
|
|
}
|
|
|
|
disconnect:
|
|
printf("eftp: requesting eft_disconnect()\n");
|
|
eft_disconnect( eft );
|
|
/* XXX why this? Without sleep, disconnect processing does not
|
|
* complete properly on the server side.
|
|
* this is probably caused by unclean termination of association regime
|
|
* (wait criteron for end of association falsly claimed to early)
|
|
*/
|
|
sleep(1);
|
|
close(s);
|
|
|
|
Ende: pid = getpid();
|
|
|
|
printf( "eftp (pid %d) terminating\n", pid);
|
|
return 0;
|
|
}
|