mirror of https://github.com/MidnightCommander/mc
292 lines
6.4 KiB
C
292 lines
6.4 KiB
C
/* Server for the Midnight Commander Virtual File System.
|
|
Routines for the tcp connection, includes the primitive rpc routines.
|
|
|
|
Copyright (C) 1995, 1996 Miguel de Icaza
|
|
|
|
This program 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 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 Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public
|
|
License along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
|
|
|
#include <config.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <signal.h>
|
|
#include <pwd.h>
|
|
#include <sys/types.h>
|
|
#include <netdb.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
|
|
#ifdef HAVE_PMAP_SET
|
|
#include <rpc/rpc.h>
|
|
#include <rpc/pmap_prot.h>
|
|
#ifdef HAVE_RPC_PMAP_CLNT_H
|
|
#include <rpc/pmap_clnt.h>
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef USE_TERMNET
|
|
#include <termnet.h>
|
|
#endif
|
|
|
|
#include <errno.h>
|
|
#include "tcputil.h"
|
|
#include "../src/dialog.h" /* for message () */
|
|
|
|
#include "utilvfs.h"
|
|
|
|
#include "mcfs.h" /* for mcserver_port definition */
|
|
|
|
#define CHECK_SIG_PIPE(sock) if (got_sigpipe) \
|
|
{ tcp_invalidate_socket (sock); return got_sigpipe = 0; }
|
|
|
|
extern void tcp_invalidate_socket (int);
|
|
extern void vfs_die (char *);
|
|
|
|
int got_sigpipe;
|
|
|
|
/* Reads a block on dest for len bytes from sock */
|
|
/* Returns a boolean indicating the success status */
|
|
int socket_read_block (int sock, char *dest, int len)
|
|
{
|
|
int nread, n;
|
|
|
|
for (nread = 0; nread < len;){
|
|
n = read (sock, dest+nread, len-nread);
|
|
if (n <= 0){
|
|
tcp_invalidate_socket (sock);
|
|
return 0;
|
|
}
|
|
nread += n;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int socket_write_block (int sock, char *buffer, int len)
|
|
{
|
|
int left, status;
|
|
|
|
for (left = len; left > 0;){
|
|
status = write (sock, buffer, left);
|
|
CHECK_SIG_PIPE (sock);
|
|
if (status < 0)
|
|
return 0;
|
|
left -= status;
|
|
buffer += status;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int send_string (int sock, char *string)
|
|
{
|
|
return socket_write_block (sock, string, strlen (string));
|
|
}
|
|
|
|
int rpc_send (int sock, ...)
|
|
{
|
|
long int tmp, len, cmd;
|
|
char *text;
|
|
va_list ap;
|
|
|
|
va_start (ap, sock);
|
|
|
|
for (;;){
|
|
cmd = va_arg (ap, int);
|
|
switch (cmd){
|
|
case RPC_END:
|
|
va_end (ap);
|
|
return 1;
|
|
|
|
case RPC_INT:
|
|
tmp = htonl (va_arg (ap, int));
|
|
write (sock, &tmp, sizeof (tmp));
|
|
CHECK_SIG_PIPE (sock);
|
|
break;
|
|
|
|
case RPC_STRING:
|
|
text = va_arg (ap, char *);
|
|
len = strlen (text);
|
|
tmp = htonl (len);
|
|
write (sock, &tmp, sizeof (tmp));
|
|
CHECK_SIG_PIPE (sock);
|
|
write (sock, text, len);
|
|
CHECK_SIG_PIPE (sock);
|
|
break;
|
|
|
|
case RPC_BLOCK:
|
|
len = va_arg (ap, int);
|
|
text = va_arg (ap, char *);
|
|
tmp = htonl (len);
|
|
write (sock, text, len);
|
|
CHECK_SIG_PIPE (sock);
|
|
break;
|
|
|
|
default:
|
|
vfs_die ("Unknown rpc message\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
typedef struct sock_callback_t {
|
|
int sock;
|
|
void (*cback)(int);
|
|
struct sock_callback_t *link;
|
|
} sock_callback_t;
|
|
|
|
sock_callback_t *sock_callbacks = 0;
|
|
|
|
static void check_hooks (int sock)
|
|
{
|
|
sock_callback_t *callback, *prev;
|
|
|
|
for (prev=callback = sock_callbacks; callback; callback = callback->link){
|
|
if (callback->sock != sock){
|
|
prev = callback;
|
|
continue;
|
|
}
|
|
callback->sock = -1;
|
|
(callback->cback)(sock);
|
|
if (callback == sock_callbacks){
|
|
sock_callbacks = callback->link;
|
|
} else {
|
|
prev->link = callback->link;
|
|
}
|
|
g_free (callback);
|
|
return;
|
|
}
|
|
}
|
|
|
|
int rpc_get (int sock, ...)
|
|
{
|
|
long int tmp, len;
|
|
char *text, **str_dest;
|
|
int *dest, cmd;
|
|
va_list ap;
|
|
|
|
va_start (ap, sock);
|
|
|
|
check_hooks (sock);
|
|
|
|
for (;;){
|
|
cmd = va_arg (ap, int);
|
|
switch (cmd){
|
|
case RPC_END:
|
|
va_end (ap);
|
|
return 1;
|
|
|
|
case RPC_INT:
|
|
if (socket_read_block (sock, (char *) &tmp, sizeof (tmp)) == 0)
|
|
return 0;
|
|
dest = va_arg (ap, int *);
|
|
*dest = ntohl (tmp);
|
|
break;
|
|
|
|
/* returns an allocated string */
|
|
case RPC_LIMITED_STRING:
|
|
case RPC_STRING:
|
|
if (socket_read_block (sock, (char *)&tmp, sizeof (tmp)) == 0)
|
|
return 0;
|
|
len = ntohl (tmp);
|
|
if (cmd == RPC_LIMITED_STRING)
|
|
if (len > 16*1024){
|
|
/* silently die */
|
|
abort ();
|
|
}
|
|
if (len > 128*1024)
|
|
abort ();
|
|
text = g_new0 (char, len+1);
|
|
if (socket_read_block (sock, text, len) == 0)
|
|
return 0;
|
|
str_dest = va_arg (ap, char **);
|
|
*str_dest = text;
|
|
text [len] = NULL;
|
|
break;
|
|
|
|
case RPC_BLOCK:
|
|
len = va_arg (ap, int);
|
|
text = va_arg (ap, char *);
|
|
if (socket_read_block (sock, text, len) == 0)
|
|
return 0;
|
|
break;
|
|
|
|
default:
|
|
vfs_die ("Unknown rpc message\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
void rpc_add_get_callback (int sock, void (*cback)(int))
|
|
{
|
|
sock_callback_t *new;
|
|
|
|
new = g_new (sock_callback_t, 1);
|
|
new->cback = cback;
|
|
new->sock = sock;
|
|
new->link = sock_callbacks;
|
|
sock_callbacks = new;
|
|
}
|
|
|
|
#if defined(IS_AIX) || defined(linux) || defined(SCO_FLAVOR) \
|
|
|| defined(__QNX__) || defined(__FreeBSD__) || defined(__bsdi__)
|
|
static void sig_pipe (int unused)
|
|
#else
|
|
static void sig_pipe (void)
|
|
#endif
|
|
{
|
|
got_sigpipe = 1;
|
|
}
|
|
|
|
void tcp_init (void)
|
|
{
|
|
struct sigaction sa;
|
|
|
|
got_sigpipe = 0;
|
|
sa.sa_handler = sig_pipe;
|
|
sa.sa_flags = 0;
|
|
sigemptyset (&sa.sa_mask);
|
|
sigaction (SIGPIPE, &sa, NULL);
|
|
}
|
|
|
|
int get_remote_port (struct sockaddr_in *sin, int *version)
|
|
{
|
|
#ifdef HAVE_PMAP_GETMAPS
|
|
int port;
|
|
struct pmaplist *pl;
|
|
|
|
*version = 1;
|
|
port = mcserver_port;
|
|
for (pl = pmap_getmaps (sin); pl; pl = pl->pml_next)
|
|
if (pl->pml_map.pm_prog == RPC_PROGNUM &&
|
|
pl->pml_map.pm_prot == IPPROTO_TCP &&
|
|
pl->pml_map.pm_vers >= *version) {
|
|
*version = pl->pml_map.pm_vers;
|
|
port = pl->pml_map.pm_port;
|
|
}
|
|
return port;
|
|
#else
|
|
#ifdef HAVE_PMAP_GETPORT
|
|
int port;
|
|
for (*version = RPC_PROGVER; *version >= 1; (*version)--)
|
|
if (port = pmap_getport (sin, RPC_PROGNUM, *version, IPPROTO_TCP))
|
|
return port;
|
|
#endif /* HAVE_PMAP_GETPORT */
|
|
#endif /* HAVE_PMAP_GETMAPS */
|
|
*version = 1;
|
|
return mcserver_port;
|
|
}
|
|
|
|
|