1 /* Copyright 1999 Peter Schlaile.
2 * Copyright 1999-2005,2007-2009 Alain Knaff.
3 * This file is part of mtools.
5 * Mtools is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * Mtools is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with Mtools. If not, see <http://www.gnu.org/licenses/>.
18 * the floppyd daemon running on the local X-Server
24 * udbz@rz.uni-karlsruhe.de
26 * Large parts of the network code shamelessly stolen from
27 * transproxy by John Saunders <john@nlc.net.au>
29 * Rewritten in C by Alain Knaff. Apparently C++ is still not as
34 #include "sysincludes.h"
39 #define USE_FLOPPYD_BUFFERED_IO 1
40 #define FLOPPYD_DEFAULT_PORT 5703
42 #include "sysincludes.h"
45 #include <X11/Xauth.h>
47 #include "floppyd_io.h"
50 #define SIGCLD SIGCHLD
53 /* For Linux 1.2.13 */
61 gcc -Wall floppyd.cpp -o floppyd -lX11
65 Communication to the clients works the following way:
67 Client sends his protocol-version. If the version between server and client
70 After that,we send our .Xauthority-file (a maximum of MAX_XAUTHORITY_LENGTH
71 Bytes long) to the server.
73 The server then checks, if it already has a .Xauthority file. If so
74 it is interpreted as LOCK-File for the floppy-device and the communication
77 (What if we have 2 floppy devices? Well. Two floppy users with different
78 home-directories should work nicely...)
80 Now, the data is written to the .Xauthority file. Then we try to open
81 a connection to the local X-Server. If this fails -> bail out.
85 The data packets are built as follows:
87 Base-packets: 1 Dword length, then data.
88 length is in Network-Byte order. (4 Bytes)
90 Commands are: 1. Packet Opcode (length 1), 1. Data packet as parameter.
92 Return: 1.Packet: 1. Dword: Bytes processed, 2. Dword: Error-Code
97 * Implement some IOCTL calls to format floppy disks or so...
98 * Read is somewhat dirty implemented. Tries multiple times to
99 read the expected bytes from the socket stream. Don't know
100 why this is necessary. Maybe the socket stream is nonblocking
101 or something IT SHOULD NOT BE!
105 typedef unsigned char Byte;
106 typedef unsigned long Dword;
107 typedef mt_off_t Qword;
109 #define MAX_XAUTHORITY_LENGTH 3000
110 #define MAX_DATA_REQUEST 3000000
111 #define BUFFERED_IO_SIZE 16348
114 void serve_client(int sock, char **device_name, int n_dev, int close_stderr);
117 #ifdef USE_FLOPPYD_BUFFERED_IO
118 typedef struct io_buffer {
119 Byte out_buffer[BUFFERED_IO_SIZE];
120 Byte in_buffer[BUFFERED_IO_SIZE];
129 static io_buffer new_io_buffer (int _handle) {
132 buffer = New(struct io_buffer);
134 buffer->handle = _handle;
135 buffer->in_valid = buffer->in_start = 0;
136 buffer->out_valid = 0;
141 static void flush(io_buffer buffer) {
142 if (buffer->out_valid) {
143 if(write(buffer->handle, buffer->out_buffer, buffer->out_valid) < 0) {
144 perror("floppyd flush");
146 buffer->out_valid = 0;
150 static void free_io_buffer(io_buffer buffer) {
156 static size_t buf_read (io_buffer buf, Byte* buffer, size_t nbytes) {
159 if (nbytes <= buf->in_valid) {
160 memcpy(buffer, buf->in_buffer+buf->in_start, nbytes);
161 buf->in_valid -= nbytes;
162 buf->in_start += nbytes;
166 memcpy(buffer, buf->in_buffer+buf->in_start,
168 nbytes -= buf->in_valid;
169 buffer += buf->in_valid;
170 if (nbytes > BUFFERED_IO_SIZE) {
171 rval = read(buf->handle, buffer, nbytes);
173 rval += buf->in_valid;
175 buf->in_valid = buf->in_start = 0;
177 rval = read(buf->handle, buf->in_buffer,
181 memcpy(buffer, buf->in_buffer, rval);
182 rval += buf->in_valid;
183 buf->in_valid = buf->in_start = 0;
186 memcpy(buffer, buf->in_buffer, nbytes);
187 buf->in_start = nbytes;
189 buf->in_valid = rval-nbytes;
198 static size_t buf_write(io_buffer buf, void* buffer, size_t nbytes) {
199 if (buf->out_valid + nbytes > BUFFERED_IO_SIZE) {
201 return write(buf->handle, buffer, nbytes);
203 memcpy(buf->out_buffer+buf->out_valid, buffer, nbytes);
204 buf->out_valid += nbytes;
212 typedef int io_buffer;
214 io_buffer new_io_buffer (int handle) {
219 size_t buf_read (io_buffer handle, Byte* buffer, size_t nbytes) {
220 return (read(handle, buffer, nbytes));
223 size_t buf_write(io_buffer handle, void* buffer, size_t nbytes) {
224 return (write(handle, buffer, nbytes));
228 void free_io_buffer(io_buffer buffer) { }
231 void flush(io_buffer buffer) { }
235 typedef struct Packet {
241 #include "byte_dword.h"
243 static Dword read_dword(io_buffer fp)
246 if (buf_read(fp, val, 4) < 4) {
250 return byte2dword(val);
253 static void write_dword(io_buffer fp, Dword parm)
257 dword2byte(parm, val);
259 buf_write(fp, val,4);
263 static Packet newPacket(void)
267 packet = New(struct Packet);
269 packet->len = packet->alloc_size = 0;
274 static void destroyPacket(Packet packet)
281 static void kill_packet(Packet packet)
287 packet->alloc_size = 0;
290 static void make_new(Packet packet, unsigned long l)
292 if (l < packet->alloc_size) {
297 packet->len = packet->alloc_size = l;
298 packet->data = malloc(l);
299 memset(packet->data, 0, l);
302 static char send_packet(Packet packet, io_buffer fp)
305 write_dword(fp, packet->len);
306 buf_write(fp, packet->data, packet->len);
309 fprintf(stderr, "send_packet(): Size: %li\n", packet->len);
313 fprintf(stderr, "send_packet(): ");
314 for (int i = 0; i < packet->len; i++) {
315 fprintf(stderr, "%d ", packet->data[i]);
317 fprintf(stderr, "\n");
321 return (packet->data != NULL);
324 static char recv_packet(Packet packet, io_buffer fp, Dword maxlength)
328 Dword length = read_dword(fp);
330 fprintf(stderr, "recv_packet(): Size: %li\n", length);
332 if (length > maxlength || length == 0xffffffff ) {
335 make_new(packet, length);
337 for (start = 0; start < length; start += l) {
338 l = buf_read(fp, packet->data+start, length-start);
343 if (packet->len == 0) {
347 fprintf(stderr, "*** read: %li\n", packet->len);
351 fprintf(stderr, "recv_packet(): ");
352 for (i = 0; i < packet->len; i++) {
353 fprintf(stderr, "%d ", packet->data[i]);
355 fprintf(stderr, "\n");
360 static void read_packet(Packet packet, int fd, int length) {
361 make_new(packet, length);
362 packet->len = read(fd, packet->data, packet->len);
365 static int write_packet(Packet packet, int fd) {
366 return (write(fd, packet->data, packet->len));
369 static void put_dword(Packet packet, int my_index, Dword val) {
370 dword2byte(val, packet->data+my_index);
373 static void put_qword(Packet packet, int my_index, Qword val) {
374 qword2byte(val, packet->data+my_index);
377 static Dword get_dword(Packet packet, int my_index) {
378 return byte2dword(packet->data+my_index);
381 static Qword get_qword(Packet packet, int my_index) {
382 return byte2qword(packet->data+my_index);
385 static Dword get_length(Packet packet) {
389 static int eat(char **ptr, int *len, unsigned char c) {
390 /* remove length + size code + terminating 0 */
398 static const char *dispName;
400 static char XAUTHORITY[]="XAUTHORITY";
402 static char do_auth(io_buffer sock, int *version)
406 Packet proto_version = newPacket();
411 char authFile[41]="/tmp/floppyd.XXXXXX";
414 Packet reply = newPacket();
418 if (!recv_packet(proto_version, sock, 4)) {
419 put_dword(reply, 0, AUTH_PACKETOVERSIZE);
420 send_packet(reply, sock);
421 destroyPacket(reply);
422 destroyPacket(proto_version);
426 *version = get_dword(proto_version, 0);
427 if (*version > FLOPPYD_PROTOCOL_VERSION ||
428 *version < FLOPPYD_PROTOCOL_VERSION_OLD) {
429 /* fail if client requests a newer version than us */
430 put_dword(reply, 0, AUTH_WRONGVERSION);
431 send_packet(reply, sock);
432 destroyPacket(reply);
433 destroyPacket(proto_version);
437 if(*version == FLOPPYD_PROTOCOL_VERSION_OLD) {
438 put_dword(reply, 0, AUTH_SUCCESS);
440 Dword cap = FLOPPYD_CAP_EXPLICIT_OPEN;
441 if(sizeof(mt_off_t) >= 8) {
442 cap |= FLOPPYD_CAP_LARGE_SEEK;
445 put_dword(reply, 0, AUTH_SUCCESS);
446 put_dword(reply, 4, FLOPPYD_PROTOCOL_VERSION);
447 put_dword(reply, 8, cap);
449 send_packet(reply, sock);
450 destroyPacket(proto_version);
453 mit_cookie = newPacket();
454 if (!recv_packet(mit_cookie, sock, MAX_XAUTHORITY_LENGTH)) {
455 put_dword(reply, 0, AUTH_PACKETOVERSIZE);
456 send_packet(reply, sock);
457 destroyPacket(reply);
458 destroyPacket(mit_cookie);
463 fd = mkstemp(authFile);
465 /* Different error than file exists */
466 put_dword(reply, 0, AUTH_DEVLOCKED);
467 send_packet(reply, sock);
469 destroyPacket(reply);
470 destroyPacket(mit_cookie);
474 setenv(XAUTHORITY, authFile, 1);
477 char *buffer=malloc(strlen(XAUTHORITY)+strlen(authFile)+2);
478 strcpy(buffer, XAUTHORITY);
480 strcat(buffer, authFile);
490 gethostname(ptr+1, 4088);
496 *ptr++ = '0'; /* Display number */
499 if(write(fd, template, len+8) < len + 8) {
503 ptr = (char *)mit_cookie->data;
504 len = mit_cookie->len;
506 if (eat(&ptr,&len,1) || /* the "type" */
507 eat(&ptr,&len,*ptr) || /* the hostname */
508 eat(&ptr,&len,*ptr)) { /* the display number */
509 destroyPacket(mit_cookie);
510 unlink(XauFileName());
511 put_dword(reply, 0, AUTH_BADPACKET);
512 send_packet(reply, sock);
513 destroyPacket(reply);
517 if(write(fd, ptr, len) < len) {
523 destroyPacket(mit_cookie);
525 displ = XOpenDisplay(dispName);
527 unlink(XauFileName());
528 put_dword(reply, 0, AUTH_AUTHFAILED);
529 send_packet(reply, sock);
530 destroyPacket(reply);
533 XCloseDisplay(displ);
535 put_dword(reply, 0, AUTH_SUCCESS);
536 send_packet(reply, sock);
537 destroyPacket(reply);
538 unlink(XauFileName());
543 * Return the port number, in network order, of the specified service.
545 static short getportnum(char *portnum)
547 char *digits = portnum;
548 struct servent *serv;
551 for (port = 0; isdigit(*digits); ++digits)
553 port = (port * 10) + (*digits - '0');
556 if ((*digits != '\0') || (port <= 0))
558 if ((serv = getservbyname(portnum, "tcp")) != NULL)
560 port = ntohs(serv->s_port);
570 fprintf(stderr, "Port lookup %s -> %hd\n", portnum, port);
577 * Return the IP address of the specified host.
579 static IPaddr_t getipaddress(char *ipaddr)
581 struct hostent *host;
584 if (((ip = inet_addr(ipaddr)) == INADDR_NONE)
586 (strcmp(ipaddr, "255.255.255.255") != 0))
588 if ((host = gethostbyname(ipaddr)) != NULL)
590 memcpy(&ip, host->h_addr, sizeof(ip));
596 fprintf(stderr, "IP lookup %s -> 0x%08lx\n", ipaddr, ip);
603 * Find the userid of the specified user.
605 static uid_t getuserid(char *user)
610 if ((pw = getpwnam(user)) != NULL)
614 else if (*user == '#')
616 uid = (uid_t)atoi(&user[1]);
620 #ifdef HAVE_GETUSERID
621 id = getuserid("nobody");
628 fprintf(stderr, "User lookup %s -> %d\n", user, uid);
637 * Find the groupid of the specified user.
639 static uid_t getgroupid(uid_t uid)
644 if ((pw = getpwuid(uid)) != NULL)
650 #ifdef HAVE_GETGROUPID
651 id = getgroupid(uid);
658 fprintf(stderr, "Group lookup %d -> %d\n", uid, gid);
667 * Bind to the specified ip and port.
669 static int bind_to_port(IPaddr_t bind_ip, short bind_port)
671 struct sockaddr_in addr;
677 if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
684 * Set the SO_REUSEADDR option for debugging.
688 if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
689 (char *)&on, sizeof(on)) < 0) {
690 perror("setsockopt");
696 * Set the address to listen to.
698 addr.sin_family = AF_INET;
699 addr.sin_port = htons(bind_port);
700 addr.sin_addr.s_addr = bind_ip;
703 * Bind our socket to the above address.
705 if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)
712 * Establish a large listen backlog.
714 if (listen(sock, SOMAXCONN) < 0)
723 static int sockethandle_now = -1;
726 * Catch alarm signals and exit.
728 static void alarm_signal(int a)
730 if (sockethandle_now != -1) {
731 close(sockethandle_now);
732 sockethandle_now = -1;
733 unlink(XauFileName());
740 * This is the main loop when running as a server.
742 static void server_main_loop(int sock, char **device_name, int n_dev)
744 struct sockaddr_in addr;
748 * Ignore dead servers so no zombies should be left hanging.
750 signal(SIGCLD, SIG_IGN);
755 * Accept an incoming connection.
758 while ((new_sock = accept(sock, (struct sockaddr *)&addr, &len)) < 0){}
761 * Create a new process to handle the connection.
767 * Under load conditions just ignore new connections.
773 * Start the proxy work in the new socket.
776 serve_client(new_sock,device_name, n_dev, 0);
782 * Close the socket as the child does the handling.
790 * Print some basic help information.
792 static void usage(char *prog, const char *opt, int ret)
796 fprintf(stderr, "%s: %s\n", prog, opt);
798 fprintf(stderr, "usage: %s [-s port [-r user] [-b ipaddr]] devicename [Names of local host]\n",
800 fprintf(stderr, " -d Run as a server (default port 5703 + DISPLAY)\n");
801 fprintf(stderr, " -s port Run as a server bound to the specified port.\n");
802 fprintf(stderr, " -r user Run as the specified user in server mode.\n");
803 fprintf(stderr, " -b ipaddr Bind to the specified ipaddr in server mode.\n");
804 fprintf(stderr, " -l Do not attempt to connect to localhost:0 to validate connection\n");
809 static char *makeDisplayName(int dispNr)
812 sprintf(result, ":%d.0", dispNr);
813 return strdup(result);
816 int main (int argc, char** argv)
820 int run_as_server = 0;
821 IPaddr_t bind_ip = INADDR_ANY;
822 unsigned short bind_port = 0;
823 uid_t run_uid = 65535;
824 gid_t run_gid = 65535;
825 char* username = strdup("nobody");
827 int port_is_supplied = 0;
829 char *server_hostname=NULL;
830 char **device_name = NULL;
831 const char *floppy0 = "/dev/fd0";
836 * Parse the command line arguments.
838 if(argc > 1 && !strcmp(argv[0], "--help"))
839 usage(argv[0], NULL, 0);
840 while ((arg = getopt(argc, argv, "ds:r:b:x:h")) != EOF)
849 port_is_supplied = 1;
850 bind_port = getportnum(optarg);
854 free(username); username = strdup(optarg);
855 run_uid = getuserid(optarg);
856 run_gid = getgroupid(run_uid);
861 bind_ip = getipaddress(optarg);
862 server_hostname=optarg;
865 dispName = strdup(optarg);
869 usage(argv[0], NULL, 0);
872 usage(argv[0], NULL, 1);
878 device_name = argv + optind;
879 n_dev = argc - optind;
881 device_name = (char **)&floppy0;
886 dispName = getenv("DISPLAY");
887 if(dispName==NULL && bind_port != 0)
888 dispName=makeDisplayName((unsigned short)(bind_port - 5703));
893 char *p = strchr(dispName,':');
894 bind_port = FLOPPYD_DEFAULT_PORT;
896 bind_port += atoi(p+1);
900 struct sockaddr_in addr;
901 unsigned int len = sizeof(addr);
903 /* try to find out port that we are connected to */
904 if(getsockname(0, (struct sockaddr*) &addr, &len) >= 0 &&
905 len == sizeof(addr)) {
906 port_is_supplied = 1;
907 bind_port = ntohs(addr.sin_port);
908 server_hostname = strdup(inet_ntoa(addr.sin_addr));
915 * Test to make sure required args were provided and are valid.
917 if (run_as_server && (bind_ip == INADDR_NONE)) {
918 usage(argv[0], "The server ipaddr is invalid.", 1);
920 if (run_as_server && (bind_port == 0)) {
921 usage(argv[0], "No server port was specified (or it was invalid).", 1);
926 * See if we should run as a server.
930 * Start by binding to the port, the child inherits this socket.
932 sock = bind_to_port(bind_ip, bind_port);
935 * Start a server process. When DEBUG is defined, just run
950 * Ignore some signals.
952 signal(SIGHUP, SIG_IGN);
954 signal(SIGINT, SIG_IGN);
956 signal(SIGQUIT, SIG_IGN);
957 signal(SIGTSTP, SIG_IGN);
958 signal(SIGCONT, SIG_IGN);
959 signal(SIGPIPE, alarm_signal);
960 /*signal(SIGALRM, alarm_signal);*/
963 * Drop back to an untrusted user.
966 initgroups(username, -1);
970 * Start a new session and group.
982 open("/dev/null", O_WRONLY);
985 * Handle the server main loop.
987 server_main_loop(sock, device_name, n_dev);
996 * Parent exits at this stage.
1001 signal(SIGHUP, alarm_signal);
1003 signal(SIGINT, alarm_signal);
1005 signal(SIGQUIT, alarm_signal);
1006 signal(SIGTERM, alarm_signal);
1007 signal(SIGTSTP, SIG_IGN);
1008 signal(SIGCONT, SIG_IGN);
1009 signal(SIGPIPE, alarm_signal);
1010 /*signal(SIGALRM, alarm_signal);*/
1012 /* Starting from inetd */
1014 serve_client(sockfd, device_name, n_dev, 1);
1018 static void send_reply(int rval, io_buffer sock, int len) {
1019 Packet reply = newPacket();
1022 put_dword(reply, 0, len);
1024 put_dword(reply, 4, 0);
1026 put_dword(reply, 4, errno);
1028 send_packet(reply, sock);
1029 destroyPacket(reply);
1032 static void send_reply64(int rval, io_buffer sock, mt_off_t len) {
1033 Packet reply = newPacket();
1035 make_new(reply, 12);
1036 put_qword(reply, 0, len);
1038 put_dword(reply, 8, 0);
1040 put_dword(reply, 8, errno);
1042 send_packet(reply, sock);
1043 destroyPacket(reply);
1046 static void cleanup(int x) {
1047 unlink(XauFileName());
1051 #include "lockdev.h"
1053 void serve_client(int sockhandle, char **device_name, int n_dev,
1063 int needSendReply=0;
1067 * Set the keepalive socket option to on.
1071 if(setsockopt(sockhandle, SOL_SOCKET,
1072 SO_KEEPALIVE, (char *)&on, sizeof(on)) < 0) {
1073 perror("setsockopt");
1083 open("/dev/null", O_WRONLY);
1087 sock = new_io_buffer(sockhandle);
1090 * Allow 60 seconds for any activity.
1095 if (!do_auth(sock, &version)) {
1096 free_io_buffer(sock);
1102 signal(SIGTERM, cleanup);
1103 signal(SIGALRM, cleanup);
1107 sockethandle_now = sockhandle;
1110 opcode = newPacket();
1117 if(version == FLOPPYD_PROTOCOL_VERSION_OLD) {
1120 devFd = open(device_name[0], O_RDWR|O_LARGEFILE);
1124 devFd = open(device_name[0],
1125 O_RDONLY|O_LARGEFILE);
1128 send_reply(0, sock, devFd);
1131 lock_dev(devFd, !readOnly, NULL);
1138 * Allow 60 seconds for any activity.
1142 if (!recv_packet(opcode, sock, 1)) {
1145 /* if(opcode->data[0] != OP_CLOSE)*/
1146 recv_packet(parm, sock, MAX_DATA_REQUEST);
1149 switch(opcode->data[0]) {
1151 if(get_length(parm) >= 4)
1152 dev_nr = get_dword(parm,0);
1155 if(dev_nr >= n_dev) {
1156 send_reply(0, sock, -1);
1160 devFd = open(device_name[dev_nr],
1161 O_RDONLY | O_LARGEFILE);
1163 fprintf(stderr, "Device opened\n");
1165 if(devFd >= 0 && lock_dev(devFd, 0, NULL)) {
1166 send_reply(0, sock, -1);
1169 send_reply(0, sock, devFd);
1173 if(get_length(parm) >= 4)
1174 dev_nr = get_dword(parm,0);
1177 if(dev_nr >= n_dev) {
1178 send_reply(0, sock, -1);
1181 devFd = open(device_name[dev_nr], O_RDWR);
1182 if(devFd >= 0 && lock_dev(devFd, 1, NULL)) {
1183 send_reply(0, sock, -1);
1186 send_reply(0, sock, devFd);
1191 fprintf(stderr, "READ:\n");
1193 read_packet(parm, devFd, get_dword(parm, 0));
1194 send_reply(devFd, sock, get_length(parm));
1195 if(get_length(parm) >= 0)
1196 send_packet(parm, sock);
1200 fprintf(stderr, "WRITE:\n");
1206 rval = write_packet(parm, devFd);
1208 send_reply(devFd, sock, rval);
1212 fprintf(stderr, "SEEK:\n");
1216 get_dword(parm, 0), get_dword(parm, 4));
1219 lseek(devFd, 0, SEEK_CUR));
1222 if(sizeof(mt_off_t) < 8) {
1224 fprintf(stderr, "64 bit requested where not available!\n");
1227 send_reply(devFd, sock, -1);
1231 fprintf(stderr, "SEEK64:\n");
1234 get_qword(parm,0), get_dword(parm,8));
1237 mt_lseek(devFd, 0, SEEK_CUR));
1241 fprintf(stderr, "FLUSH:\n");
1244 send_reply(devFd, sock, 0);
1248 fprintf(stderr, "CLOSE:\n");
1258 /* Unimplemented for now... */
1262 fprintf(stderr, "Invalid Opcode!\n");
1265 send_reply(devFd, sock, -1);
1275 fprintf(stderr, "Closing down...\n");
1283 free_io_buffer(sock);
1285 /* remove "Lock"-File */
1286 unlink(XauFileName());
1289 send_reply(rval, sock, 0);
1290 destroyPacket(opcode);
1291 destroyPacket(parm);
1297 int main(int argc, char **argv)
1299 puts("Floppyd support not included!");