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");
828 char **device_name = NULL;
829 const char *floppy0 = "/dev/fd0";
834 * Parse the command line arguments.
836 if(argc > 1 && !strcmp(argv[0], "--help"))
837 usage(argv[0], NULL, 0);
838 while ((arg = getopt(argc, argv, "ds:r:b:x:h")) != EOF)
847 bind_port = getportnum(optarg);
851 free(username); username = strdup(optarg);
852 run_uid = getuserid(optarg);
853 run_gid = getgroupid(run_uid);
858 bind_ip = getipaddress(optarg);
861 dispName = strdup(optarg);
865 usage(argv[0], NULL, 0);
868 usage(argv[0], NULL, 1);
874 device_name = argv + optind;
875 n_dev = argc - optind;
877 device_name = (char **)&floppy0;
882 dispName = getenv("DISPLAY");
883 if(dispName==NULL && bind_port != 0)
884 dispName=makeDisplayName((unsigned short)(bind_port - 5703));
889 char *p = strchr(dispName,':');
890 bind_port = FLOPPYD_DEFAULT_PORT;
892 bind_port += atoi(p+1);
896 struct sockaddr_in addr;
897 unsigned int len = sizeof(addr);
899 /* try to find out port that we are connected to */
900 if(getsockname(0, (struct sockaddr*) &addr, &len) >= 0 &&
901 len == sizeof(addr)) {
902 bind_port = ntohs(addr.sin_port);
909 * Test to make sure required args were provided and are valid.
911 if (run_as_server && (bind_ip == INADDR_NONE)) {
912 usage(argv[0], "The server ipaddr is invalid.", 1);
914 if (run_as_server && (bind_port == 0)) {
915 usage(argv[0], "No server port was specified (or it was invalid).", 1);
920 * See if we should run as a server.
924 * Start by binding to the port, the child inherits this socket.
926 sock = bind_to_port(bind_ip, bind_port);
929 * Start a server process. When DEBUG is defined, just run
944 * Ignore some signals.
946 signal(SIGHUP, SIG_IGN);
948 signal(SIGINT, SIG_IGN);
950 signal(SIGQUIT, SIG_IGN);
951 signal(SIGTSTP, SIG_IGN);
952 signal(SIGCONT, SIG_IGN);
953 signal(SIGPIPE, alarm_signal);
954 /*signal(SIGALRM, alarm_signal);*/
957 * Drop back to an untrusted user.
960 initgroups(username, -1);
964 * Start a new session and group.
976 open("/dev/null", O_WRONLY);
979 * Handle the server main loop.
981 server_main_loop(sock, device_name, n_dev);
990 * Parent exits at this stage.
995 signal(SIGHUP, alarm_signal);
997 signal(SIGINT, alarm_signal);
999 signal(SIGQUIT, alarm_signal);
1000 signal(SIGTERM, alarm_signal);
1001 signal(SIGTSTP, SIG_IGN);
1002 signal(SIGCONT, SIG_IGN);
1003 signal(SIGPIPE, alarm_signal);
1004 /*signal(SIGALRM, alarm_signal);*/
1006 /* Starting from inetd */
1008 serve_client(sockfd, device_name, n_dev, 1);
1012 static void send_reply(int rval, io_buffer sock, int len) {
1013 Packet reply = newPacket();
1016 put_dword(reply, 0, len);
1018 put_dword(reply, 4, 0);
1020 put_dword(reply, 4, errno);
1022 send_packet(reply, sock);
1023 destroyPacket(reply);
1026 static void send_reply64(int rval, io_buffer sock, mt_off_t len) {
1027 Packet reply = newPacket();
1029 make_new(reply, 12);
1030 put_qword(reply, 0, len);
1032 put_dword(reply, 8, 0);
1034 put_dword(reply, 8, errno);
1036 send_packet(reply, sock);
1037 destroyPacket(reply);
1040 static void cleanup(int x) {
1041 unlink(XauFileName());
1045 #include "lockdev.h"
1047 void serve_client(int sockhandle, char **device_name, int n_dev,
1057 int needSendReply=0;
1061 * Set the keepalive socket option to on.
1065 if(setsockopt(sockhandle, SOL_SOCKET,
1066 SO_KEEPALIVE, (char *)&on, sizeof(on)) < 0) {
1067 perror("setsockopt");
1077 open("/dev/null", O_WRONLY);
1081 sock = new_io_buffer(sockhandle);
1084 * Allow 60 seconds for any activity.
1089 if (!do_auth(sock, &version)) {
1090 free_io_buffer(sock);
1096 signal(SIGTERM, cleanup);
1097 signal(SIGALRM, cleanup);
1101 sockethandle_now = sockhandle;
1104 opcode = newPacket();
1111 if(version == FLOPPYD_PROTOCOL_VERSION_OLD) {
1114 devFd = open(device_name[0], O_RDWR|O_LARGEFILE);
1118 devFd = open(device_name[0],
1119 O_RDONLY|O_LARGEFILE);
1122 send_reply(0, sock, devFd);
1125 lock_dev(devFd, !readOnly, NULL);
1132 * Allow 60 seconds for any activity.
1136 if (!recv_packet(opcode, sock, 1)) {
1139 /* if(opcode->data[0] != OP_CLOSE)*/
1140 recv_packet(parm, sock, MAX_DATA_REQUEST);
1143 switch(opcode->data[0]) {
1145 if(get_length(parm) >= 4)
1146 dev_nr = get_dword(parm,0);
1149 if(dev_nr >= n_dev) {
1150 send_reply(0, sock, -1);
1154 devFd = open(device_name[dev_nr],
1155 O_RDONLY | O_LARGEFILE);
1157 fprintf(stderr, "Device opened\n");
1159 if(devFd >= 0 && lock_dev(devFd, 0, NULL)) {
1160 send_reply(0, sock, -1);
1163 send_reply(0, sock, devFd);
1167 if(get_length(parm) >= 4)
1168 dev_nr = get_dword(parm,0);
1171 if(dev_nr >= n_dev) {
1172 send_reply(0, sock, -1);
1175 devFd = open(device_name[dev_nr], O_RDWR);
1176 if(devFd >= 0 && lock_dev(devFd, 1, NULL)) {
1177 send_reply(0, sock, -1);
1180 send_reply(0, sock, devFd);
1185 fprintf(stderr, "READ:\n");
1187 read_packet(parm, devFd, get_dword(parm, 0));
1188 send_reply(devFd, sock, get_length(parm));
1189 if(get_length(parm) >= 0)
1190 send_packet(parm, sock);
1194 fprintf(stderr, "WRITE:\n");
1200 rval = write_packet(parm, devFd);
1202 send_reply(devFd, sock, rval);
1206 fprintf(stderr, "SEEK:\n");
1210 get_dword(parm, 0), get_dword(parm, 4));
1213 lseek(devFd, 0, SEEK_CUR));
1216 if(sizeof(mt_off_t) < 8) {
1218 fprintf(stderr, "64 bit requested where not available!\n");
1221 send_reply(devFd, sock, -1);
1225 fprintf(stderr, "SEEK64:\n");
1228 get_qword(parm,0), get_dword(parm,8));
1231 mt_lseek(devFd, 0, SEEK_CUR));
1235 fprintf(stderr, "FLUSH:\n");
1238 send_reply(devFd, sock, 0);
1242 fprintf(stderr, "CLOSE:\n");
1252 /* Unimplemented for now... */
1256 fprintf(stderr, "Invalid Opcode!\n");
1259 send_reply(devFd, sock, -1);
1269 fprintf(stderr, "Closing down...\n");
1277 free_io_buffer(sock);
1279 /* remove "Lock"-File */
1280 unlink(XauFileName());
1283 send_reply(rval, sock, 0);
1284 destroyPacket(opcode);
1285 destroyPacket(parm);
1291 int main(int argc, char **argv)
1293 puts("Floppyd support not included!");