1/*  Copyright 1999 Peter Schlaile.
2 *  Copyright 1999-2002,2005-2007,2009 Alain Knaff.
3 *  This file is part of mtools.
4 *
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.
9 *
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.
14 *
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/>.
17 *
18 * IO to the floppyd daemon running on the local X-Server Host
19 *
20 * written by:
21 *
22 * Peter Schlaile
23 *
24 * udbz@rz.uni-karlsruhe.de
25 *
26 */
27
28#include "sysincludes.h"
29#include "stream.h"
30#include "mtools.h"
31#include "msdos.h"
32#include "scsi.h"
33#include "partition.h"
34#include "floppyd_io.h"
35
36#ifdef USE_FLOPPYD
37
38/* ######################################################################## */
39
40
41typedef unsigned char Byte;
42typedef unsigned long Dword;
43
44const char* AuthErrors[] = {
45	"Auth success!",
46	"Auth failed: Packet oversized!",
47	"Auth failed: X-Cookie doesn't match!",
48	"Auth failed: Wrong transmission protocol version!",
49	"Auth failed: Device locked!"
50};
51
52
53typedef struct RemoteFile_t {
54	Class_t *Class;
55	int refs;
56	Stream_t *Next;
57	Stream_t *Buffer;
58	int fd;
59	mt_off_t offset;
60	mt_off_t lastwhere;
61	mt_off_t size;
62	int version;
63	int capabilities;
64	int drive;
65} RemoteFile_t;
66
67
68#include "byte_dword.h"
69#include "read_dword.h"
70
71
72/* ######################################################################## */
73
74static int authenticate_to_floppyd(RemoteFile_t *floppyd, int sock, char *display)
75{
76	off_t filelen;
77	Byte buf[16];
78	const char *command[] = { "xauth", "xauth", "extract", "-", 0, 0 };
79	char *xcookie;
80	Dword errcode;
81	int l;
82
83	command[4] = display;
84
85	filelen=strlen(display);
86	filelen += 100;
87
88	xcookie = (char *) safe_malloc(filelen+4);
89	filelen = safePopenOut(command, xcookie+4, filelen);
90	if(filelen < 1)
91		return AUTH_AUTHFAILED;
92
93	/* Version negotiation */
94	dword2byte(4,buf);
95	dword2byte(floppyd->version,buf+4);
96	write(sock, buf, 8);
97
98	if ( (l = read_dword(sock)) < 4) {
99		return AUTH_WRONGVERSION;
100	}
101
102	errcode = read_dword(sock);
103
104	if (errcode != AUTH_SUCCESS) {
105		return errcode;
106	}
107
108	if(l >= 8)
109		floppyd->version = read_dword(sock);
110	if(l >= 12)
111		floppyd->capabilities = read_dword(sock);
112
113	dword2byte(filelen, (Byte *)xcookie);
114	write(sock, xcookie, filelen+4);
115
116	if (read_dword(sock) != 4) {
117		return AUTH_PACKETOVERSIZE;
118	}
119
120	errcode = read_dword(sock);
121
122	return errcode;
123}
124
125
126static int floppyd_reader(int fd, char* buffer, int len)
127{
128	Dword errcode;
129	Dword gotlen;
130	int l;
131	int start;
132	Byte buf[16];
133
134	dword2byte(1, buf);
135	buf[4] = OP_READ;
136	dword2byte(4, buf+5);
137	dword2byte(len, buf+9);
138	write(fd, buf, 13);
139
140	if (read_dword(fd) != 8) {
141		errno = EIO;
142		return -1;
143	}
144
145	gotlen = read_dword(fd);
146	errcode = read_dword(fd);
147
148	if (gotlen != -1) {
149		if (read_dword(fd) != gotlen) {
150			errno = EIO;
151			return -1;
152		}
153		for (start = 0, l = 0; start < gotlen; start += l) {
154			l = read(fd, buffer+start, gotlen-start);
155			if (l == 0) {
156				errno = EIO;
157				return -1;
158			}
159		}
160	} else {
161		errno = errcode;
162	}
163	return gotlen;
164}
165
166static int floppyd_writer(int fd, char* buffer, int len)
167{
168	Dword errcode;
169	Dword gotlen;
170	Byte buf[16];
171
172	dword2byte(1, buf);
173	buf[4] = OP_WRITE;
174	dword2byte(len, buf+5);
175
176	write(fd, buf, 9);
177        write(fd, buffer, len);
178
179	if (read_dword(fd) != 8) {
180		errno = EIO;
181		return -1;
182	}
183
184	gotlen = read_dword(fd);
185	errcode = read_dword(fd);
186
187	errno = errcode;
188	if(errno != 0 && gotlen == 0) {
189	    if (errno == EBADF)
190		errno = EROFS;
191	    gotlen = -1;
192	}
193
194	return gotlen;
195}
196
197static int floppyd_lseek(int fd, mt_off_t offset, int whence)
198{
199	Dword errcode;
200	Dword gotlen;
201	Byte buf[32];
202
203	dword2byte(1, buf);
204	buf[4] = OP_SEEK;
205
206	dword2byte(8, buf+5);
207	dword2byte(truncBytes32(offset), buf+9);
208	dword2byte(whence, buf+13);
209
210	write(fd, buf, 17);
211
212	if (read_dword(fd) != 8) {
213		errno = EIO;
214		return -1;
215	}
216
217	gotlen = read_dword(fd);
218	errcode = read_dword(fd);
219
220	errno = errcode;
221
222	return gotlen;
223}
224
225static int floppyd_open(RemoteFile_t *This, int mode)
226{
227	Dword errcode;
228	Dword gotlen;
229	Byte buf[16];
230
231	if(! (This->capabilities & FLOPPYD_CAP_EXPLICIT_OPEN) ) {
232		/* floppyd has no "explicit seek" capabilities */
233		return 0;
234	}
235
236	dword2byte(1, buf);
237	if((mode & O_ACCMODE) == O_RDONLY)
238		buf[4] = OP_OPRO;
239	else
240		buf[4] = OP_OPRW;
241	dword2byte(4, buf+5);
242	dword2byte(This->drive, buf+9);
243
244	write(This->fd, buf, 13);
245
246	if (read_dword(This->fd) != 8) {
247		errno = EIO;
248		return -1;
249	}
250
251	gotlen = read_dword(This->fd);
252	errcode = read_dword(This->fd);
253
254	errno = errcode;
255
256	return gotlen;
257}
258
259
260/* ######################################################################## */
261
262typedef int (*iofn) (int, char *, int);
263
264static int floppyd_io(Stream_t *Stream, char *buf, mt_off_t where, int len,
265		   iofn io)
266{
267	DeclareThis(RemoteFile_t);
268	int ret;
269
270	where += This->offset;
271
272	if (where != This->lastwhere ){
273		if(floppyd_lseek( This->fd, where, SEEK_SET) < 0 ){
274			perror("floppyd_lseek");
275			This->lastwhere = (mt_off_t) -1;
276			return -1;
277		}
278	}
279	ret = io(This->fd, buf, len);
280	if ( ret == -1 ){
281		perror("floppyd_io");
282		This->lastwhere = (mt_off_t) -1;
283		return -1;
284	}
285	This->lastwhere = where + ret;
286	return ret;
287}
288
289static int floppyd_read(Stream_t *Stream, char *buf, mt_off_t where, size_t len)
290{
291	return floppyd_io(Stream, buf, where, len, (iofn) floppyd_reader);
292}
293
294static int floppyd_write(Stream_t *Stream, char *buf, mt_off_t where, size_t len)
295{
296	return floppyd_io(Stream, buf, where, len, (iofn) floppyd_writer);
297}
298
299static int floppyd_flush(Stream_t *Stream)
300{
301	Byte buf[16];
302
303	DeclareThis(RemoteFile_t);
304
305	dword2byte(1, buf);
306	buf[4] = OP_FLUSH;
307	dword2byte(1, buf+5);
308	buf[9] = '\0';
309
310	write(This->fd, buf, 10);
311
312	if (read_dword(This->fd) != 8) {
313		errno = EIO;
314		return -1;
315	}
316
317	read_dword(This->fd);
318	read_dword(This->fd);
319	return 0;
320}
321
322static int floppyd_free(Stream_t *Stream)
323{
324	Byte buf[16];
325	int gotlen;
326	int errcode;
327	DeclareThis(RemoteFile_t);
328
329	if (This->fd > 2) {
330		dword2byte(1, buf);
331		buf[4] = OP_CLOSE;
332		write(This->fd, buf, 5);
333		shutdown(This->fd, 1);
334		if (read_dword(This->fd) != 8) {
335		    errno = EIO;
336		    return -1;
337		}
338
339		gotlen = read_dword(This->fd);
340		errcode = read_dword(This->fd);
341
342		errno = errcode;
343
344		close(This->fd);
345		return gotlen;
346	} else {
347		return 0;
348	}
349}
350
351static int floppyd_geom(Stream_t *Stream, struct device *dev,
352		     struct device *orig_dev,
353		     int media, struct bootsector *boot)
354{
355	size_t tot_sectors;
356	int sect_per_track;
357	DeclareThis(RemoteFile_t);
358
359	dev->ssize = 2; /* allow for init_geom to change it */
360	dev->use_2m = 0x80; /* disable 2m mode to begin */
361
362	if(media == 0xf0 || media >= 0x100){
363		dev->heads = WORD(nheads);
364		dev->sectors = WORD(nsect);
365		tot_sectors = DWORD(bigsect);
366		SET_INT(tot_sectors, WORD(psect));
367		sect_per_track = dev->heads * dev->sectors;
368		tot_sectors += sect_per_track - 1; /* round size up */
369		dev->tracks = tot_sectors / sect_per_track;
370
371	} else if (media >= 0xf8){
372		media &= 3;
373		dev->heads = old_dos[media].heads;
374		dev->tracks = old_dos[media].tracks;
375		dev->sectors = old_dos[media].sectors;
376		dev->ssize = 0x80;
377		dev->use_2m = ~1;
378	} else {
379		fprintf(stderr,"Unknown media type\n");
380		exit(1);
381	}
382
383	This->size = (mt_off_t) 512 * dev->sectors * dev->tracks * dev->heads;
384
385	return 0;
386}
387
388
389static int floppyd_data(Stream_t *Stream, time_t *date, mt_size_t *size,
390		     int *type, int *address)
391{
392	DeclareThis(RemoteFile_t);
393
394	if(date)
395		/* unknown, and irrelevant anyways */
396		*date = 0;
397	if(size)
398		/* the size derived from the geometry */
399		*size = (mt_size_t) This->size;
400	if(type)
401		*type = 0; /* not a directory */
402	if(address)
403		*address = 0;
404	return 0;
405}
406
407/* ######################################################################## */
408
409static Class_t FloppydFileClass = {
410	floppyd_read,
411	floppyd_write,
412	floppyd_flush,
413	floppyd_free,
414	floppyd_geom,
415	floppyd_data
416};
417
418/* ######################################################################## */
419
420static int get_host_and_port_and_drive(const char* name, char** hostname,
421				       char **display, short* port, int *drive)
422{
423	char* newname = strdup(name);
424	char* p;
425	char* p2;
426
427	p = newname;
428	while (*p != '/' && *p) p++;
429	p2 = p;
430	if (*p) p++;
431	*p2 = 0;
432
433	*port = FLOPPYD_DEFAULT_PORT;
434	if(*p >= '0' && *p <= '9')
435	  *port = strtoul(p, &p, 0);
436	if(*p == '/')
437	  p++;
438	*drive = 0;
439	if(*p >= '0' && *p <= '9')
440	  *drive = strtoul(p, &p, 0);
441
442	*display = strdup(newname);
443
444	p = newname;
445	while (*p != ':' && *p) p++;
446	p2 = p;
447	if (*p) p++;
448	*p2 = 0;
449
450	*port += atoi(p);  /* add display number to the port */
451
452	if (!*newname || strcmp(newname, "unix") == 0) {
453		free(newname);
454		newname = strdup("localhost");
455	}
456
457	*hostname = newname;
458	return 1;
459}
460
461/*
462 *  * Return the IP address of the specified host.
463 *  */
464static IPaddr_t getipaddress(char *ipaddr)
465{
466
467	struct hostent  *host;
468	IPaddr_t        ip;
469
470	if (((ip = inet_addr(ipaddr)) == INADDR_NONE) &&
471	    (strcmp(ipaddr, "255.255.255.255") != 0)) {
472
473		if ((host = gethostbyname(ipaddr)) != NULL) {
474			memcpy(&ip, host->h_addr, sizeof(ip));
475		}
476
477		endhostent();
478	}
479
480#ifdef DEBUG
481	fprintf(stderr, "IP lookup %s -> 0x%08lx\n", ipaddr, ip);
482#endif
483
484	return (ip);
485}
486
487/*
488 *  * Connect to the floppyd server.
489 *  */
490static int connect_to_server(IPaddr_t ip, short port)
491{
492
493	struct sockaddr_in      addr;
494	int                     sock;
495
496	/*
497	 * Allocate a socket.
498	 */
499	if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
500		return (-1);
501	}
502
503	/*
504	 * Set the address to connect to.
505	 */
506
507	addr.sin_family = AF_INET;
508	addr.sin_port = htons(port);
509	addr.sin_addr.s_addr = ip;
510
511        /*
512	 * Connect our socket to the above address.
513	 */
514	if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
515		return (-1);
516	}
517
518        /*
519	 * Set the keepalive socket option to on.
520	 */
521	{
522		int             on = 1;
523		setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE,
524			   (char *)&on, sizeof(on));
525	}
526
527	return (sock);
528}
529
530static int ConnectToFloppyd(RemoteFile_t *floppyd, const char* name,
531			    char *errmsg);
532
533Stream_t *FloppydOpen(struct device *dev, struct device *dev2,
534		      char *name, int mode, char *errmsg,
535		      int mode2, int locked)
536{
537	RemoteFile_t *This;
538
539	if (!dev ||  !(dev->misc_flags & FLOPPYD_FLAG))
540		return NULL;
541
542	This = New(RemoteFile_t);
543	if (!This){
544		printOom();
545		return NULL;
546	}
547	This->Class = &FloppydFileClass;
548	This->Next = 0;
549	This->offset = 0;
550	This->lastwhere = 0;
551	This->refs = 1;
552	This->Buffer = 0;
553
554	This->fd = ConnectToFloppyd(This, name, errmsg);
555	if (This->fd == -1) {
556		Free(This);
557		return NULL;
558	}
559
560	if(floppyd_open(This, mode) < 0) {
561		sprintf(errmsg,
562			"Can't open remote drive: %s", strerror(errno));
563		close(This->fd);
564		Free(This);
565		return NULL;
566	}
567
568	return (Stream_t *) This;
569}
570
571static int ConnectToFloppyd(RemoteFile_t *floppyd, const char* name,
572			    char *errmsg)
573{
574	char* hostname;
575	char* display;
576	short port;
577	int rval = get_host_and_port_and_drive(name, &hostname, &display,
578					       &port, &floppyd->drive);
579	int sock;
580	int reply;
581
582	if (!rval) return -1;
583
584	floppyd->version = FLOPPYD_PROTOCOL_VERSION;
585	floppyd->capabilities = 0;
586	while(1) {
587		sock = connect_to_server(getipaddress(hostname), port);
588
589		if (sock == -1) {
590#ifdef HAVE_SNPRINTF
591			snprintf(errmsg, 200,
592				 "Can't connect to floppyd server on %s, port %i (%s)!",
593				 hostname, port, strerror(errno));
594#else
595			sprintf(errmsg,
596				 "Can't connect to floppyd server on %s, port %i!",
597				 hostname, port);
598#endif
599			return -1;
600		}
601
602		reply = authenticate_to_floppyd(floppyd, sock, display);
603		if(floppyd->version == FLOPPYD_PROTOCOL_VERSION_OLD)
604			break;
605		if(reply == AUTH_WRONGVERSION) {
606			/* fall back on old version */
607			floppyd->version = FLOPPYD_PROTOCOL_VERSION_OLD;
608			continue;
609		}
610		break;
611	}
612
613	if (reply != 0) {
614		fprintf(stderr,
615			"Permission denied, authentication failed!\n"
616			"%s\n", AuthErrors[reply]);
617		return -1;
618	}
619
620	free(hostname);
621	free(display);
622
623	return sock;
624}
625#endif
626