1/*	$OpenBSD: mtrmt.c,v 1.25 2023/03/08 04:43:04 guenther Exp $	*/
2/*	$NetBSD: mtrmt.c,v 1.2 1996/03/06 06:22:07 scottr Exp $	*/
3
4/*-
5 * Copyright (c) 1980, 1993
6 *	The Regents of the University of California.  All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#include <sys/types.h>
34#include <sys/mtio.h>
35#include <sys/ioctl.h>
36#include <sys/socket.h>
37#include <sys/time.h>
38#include <ufs/ufs/dinode.h>
39
40#include <netinet/in.h>
41#include <netinet/tcp.h>
42
43#include <protocols/dumprestore.h>
44
45#include <ctype.h>
46#include <err.h>
47#include <netdb.h>
48#include <pwd.h>
49#include <signal.h>
50#include <stdio.h>
51#include <stdlib.h>
52#include <string.h>
53#include <unistd.h>
54#include <limits.h>
55
56#include "pathnames.h"
57#include "mt.h"
58
59#define	TS_CLOSED	0
60#define	TS_OPEN		1
61
62static	int rmtstate = TS_CLOSED;
63static	int rmtape;
64static	char *rmtpeer;
65
66static	int okname(char *);
67static	int rmtcall(char *, char *);
68static	void rmtconnaborted(void);
69static	void sigrmtconnaborted(int);
70static	int rmtgetb(void);
71static	void rmtgetconn(void);
72static	void rmtgets(char *, int);
73static	int rmtreply(char *);
74
75int
76rmthost(char *host)
77{
78	if ((rmtpeer = strdup(host)) == NULL)
79		err(1, "strdup");
80	signal(SIGPIPE, sigrmtconnaborted);
81	rmtgetconn();
82	if (rmtape < 0)
83		return (0);
84	return (1);
85}
86
87static void
88sigrmtconnaborted(int signo)
89{
90
91	warnx("Lost connection to remote host.");
92	_exit(1);
93}
94
95static void
96rmtconnaborted(void)
97{
98
99	errx(1, "Lost connection to remote host.");
100}
101
102void
103rmtgetconn(void)
104{
105	char *cp;
106	static struct passwd *pwd = NULL;
107#ifdef notdef
108	static int on = 1;
109#endif
110	char *tuser;
111	int size;
112	int maxseg;
113
114	if ((cp = strchr(rmtpeer, '@')) != NULL) {
115		tuser = rmtpeer;
116		*cp = '\0';
117		if (!okname(tuser))
118			exit(1);
119		rmtpeer = ++cp;
120	} else
121		tuser = pwd->pw_name;
122
123	rmtape = rcmdsh(&rmtpeer, -1, pwd->pw_name, tuser,
124	    _PATH_RMT, NULL);
125	if (rmtape == -1)
126		exit(1);		/* rcmd already printed error message */
127
128	size = TP_BSIZE;
129	if (size > 60 * 1024)		/* XXX */
130		size = 60 * 1024;
131	/* Leave some space for rmt request/response protocol */
132	size += 2 * 1024;
133
134	while (size > TP_BSIZE &&
135	    setsockopt(rmtape, SOL_SOCKET, SO_SNDBUF, &size, sizeof (size)) == -1)
136		    size -= TP_BSIZE;
137	(void)setsockopt(rmtape, SOL_SOCKET, SO_RCVBUF, &size, sizeof (size));
138
139	maxseg = 1024;
140	(void)setsockopt(rmtape, IPPROTO_TCP, TCP_MAXSEG, &maxseg,
141	    sizeof (maxseg));
142
143#ifdef notdef
144	if (setsockopt(rmtape, IPPROTO_TCP, TCP_NODELAY, &on, sizeof (on)) == -1)
145		perror("TCP_NODELAY setsockopt");
146#endif
147	if (pledge("stdio", NULL) == -1)
148		err(1, "pledge");
149}
150
151static int
152okname(char *cp0)
153{
154	unsigned char *cp;
155	int c;
156
157	for (cp = cp0; *cp; cp++) {
158		c = (unsigned char)*cp;
159		if (!isascii(c) || !(isalnum(c) || c == '_' || c == '-')) {
160			warnx("invalid user name: %s", cp0);
161			return (0);
162		}
163	}
164	return (1);
165}
166
167int
168rmtopen(char *tape, int mode)
169{
170	char buf[1 + PATH_MAX+1 + 10+1 +1];
171	int r;
172
173	r = snprintf(buf, sizeof (buf), "O%s\n%d\n", tape, mode);
174	if (r < 0 || r >= sizeof buf)
175		errx(1, "tape name too long");
176	rmtstate = TS_OPEN;
177	return (rmtcall(tape, buf));
178}
179
180void
181rmtclose(void)
182{
183
184	if (rmtstate != TS_OPEN)
185		return;
186	rmtcall("close", "C\n");
187	rmtstate = TS_CLOSED;
188}
189
190struct	mtget mts;
191
192struct mtget *
193rmtstatus(void)
194{
195	int i;
196	char *cp;
197
198	if (rmtstate != TS_OPEN)
199		return (NULL);
200	rmtcall("status", "S\n");
201	for (i = 0, cp = (char *)&mts; i < sizeof(mts); i++)
202		*cp++ = rmtgetb();
203	return (&mts);
204}
205
206int
207rmtioctl(int cmd, int count)
208{
209	char buf[1 + 10+1 + 10+1 +1];
210	int r;
211
212	if (count < 0)
213		return (-1);
214	r = snprintf(buf, sizeof (buf), "I%d\n%d\n", cmd, count);
215	if (r < 0 || r >= sizeof buf)
216		errx(1, "string error during ioctl");
217	return (rmtcall("ioctl", buf));
218}
219
220static int
221rmtcall(char *cmd, char *buf)
222{
223
224	if (write(rmtape, buf, strlen(buf)) != strlen(buf))
225		rmtconnaborted();
226	return (rmtreply(cmd));
227}
228
229static int
230rmtreply(char *cmd)
231{
232	char *cp;
233	char code[30], emsg[BUFSIZ];
234
235	rmtgets(code, sizeof (code));
236	if (*code == 'E' || *code == 'F') {
237		rmtgets(emsg, sizeof (emsg));
238		warnx("%s: %s", cmd, emsg);
239		if (*code == 'F') {
240			rmtstate = TS_CLOSED;
241			return (-1);
242		}
243		return (-1);
244	}
245	if (*code != 'A') {
246		/* Kill trailing newline */
247		cp = code + strlen(code);
248		if (cp > code && *--cp == '\n')
249			*cp = '\0';
250
251		warnx("Protocol to remote tape server botched (code \"%s\").",
252		    code);
253		rmtconnaborted();
254	}
255	return (atoi(code + 1));
256}
257
258int
259rmtgetb(void)
260{
261	char c;
262
263	if (read(rmtape, &c, 1) != 1)
264		rmtconnaborted();
265	return (c);
266}
267
268/* Get a line (guaranteed to have a trailing newline). */
269void
270rmtgets(char *line, int len)
271{
272	char *cp = line;
273
274	while (len > 1) {
275		*cp = rmtgetb();
276		if (*cp == '\n') {
277			cp[1] = '\0';
278			return;
279		}
280		cp++;
281		len--;
282	}
283	*cp = '\0';
284	warnx("Protocol to remote tape server botched.");
285	warnx("(rmtgets got \"%s\").", line);
286	rmtconnaborted();
287}
288