1/*	$OpenBSD: dumprmt.c,v 1.33 2023/12/21 08:01:21 otto Exp $	*/
2/*	$NetBSD: dumprmt.c,v 1.17 1997/06/05 16:10:47 mrg 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/param.h>	/* MAXBSIZE */
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 <errno.h>
49#include <pwd.h>
50#include <signal.h>
51#include <stdio.h>
52#include <stdlib.h>
53#include <string.h>
54#include <unistd.h>
55#include <limits.h>
56
57#include "pathnames.h"
58#include "dump.h"
59
60#define	TS_CLOSED	0
61#define	TS_OPEN		1
62
63static	int rmtstate = TS_CLOSED;
64static	int rmtape;
65static	char *rmtpeer;
66
67static	int okname(char *);
68static	int rmtcall(char *, char *);
69static	void rmtconnaborted(int);
70static	int rmtgetb(void);
71static	void rmtgetconn(void);
72static	void rmtgets(char *, int);
73static	int rmtreply(char *);
74
75int	rmthost(char *host);
76int	rmtopen(char *tape, int mode);
77int	rmtread(char *buf, int count);
78int	rmtwrite(char *buf, int count);
79int	rmtseek(int offset, int pos);
80int	rmtioctl(int cmd, int count);
81void	rmtclose(void);
82
83
84int
85rmthost(char *host)
86{
87	int len = strlen(host) + 1;
88
89	rmtpeer = malloc(len);
90	if (rmtpeer)
91		strlcpy(rmtpeer, host, len);
92	else
93		return (0);
94	signal(SIGPIPE, rmtconnaborted);
95	rmtgetconn();
96	if (rmtape < 0)
97		return (0);
98	return (1);
99}
100
101static void
102rmtconnaborted(int signo)
103{
104	/* XXX signal race */
105	errx(X_ABORT, "Lost connection to remote host.");
106}
107
108void
109rmtgetconn(void)
110{
111	char *cp;
112	static struct servent *sp = NULL;
113	static struct passwd *pwd = NULL;
114	static int on = 1;
115	char *tuser, *name;
116	int size;
117	int maxseg;
118
119	if (sp == NULL) {
120		sp = getservbyname("shell", "tcp");
121		if (sp == NULL)
122			errx(X_STARTUP, "shell/tcp: unknown service");
123		pwd = getpwuid(getuid());
124		if (pwd == NULL)
125			errx(X_STARTUP, "who are you?");
126	}
127	if ((name = strdup(pwd->pw_name)) == NULL)
128		err(X_STARTUP, "malloc");
129	if ((cp = strchr(rmtpeer, '@')) != NULL) {
130		tuser = rmtpeer;
131		*cp = '\0';
132		if (!okname(tuser))
133			exit(X_STARTUP);
134		rmtpeer = ++cp;
135	} else
136		tuser = name;
137
138	rmtape = rcmd(&rmtpeer, sp->s_port, name, tuser, _PATH_RMT, NULL);
139	(void)free(name);
140	if (rmtape < 0)
141		return;
142
143	size = ntrec * TP_BSIZE;
144	if (size > 60 * 1024)		/* XXX */
145		size = 60 * 1024;
146	/* Leave some space for rmt request/response protocol */
147	size += 2 * 1024;
148	while (size > TP_BSIZE &&
149	    setsockopt(rmtape, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)) == -1)
150		    size -= TP_BSIZE;
151	(void)setsockopt(rmtape, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
152
153	maxseg = 1024;
154	(void)setsockopt(rmtape, IPPROTO_TCP, TCP_MAXSEG, &maxseg,
155		sizeof(maxseg));
156
157	(void) setsockopt(rmtape, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));
158}
159
160static int
161okname(char *cp0)
162{
163	char *cp;
164	unsigned char c;
165
166	for (cp = cp0; *cp; cp++) {
167		c = *cp;
168		if (!isascii(c) || !(isalnum(c) || c == '_' || c == '-')) {
169			warnx("invalid user name: %s", cp0);
170			return (0);
171		}
172	}
173	return (1);
174}
175
176int
177rmtopen(char *tape, int mode)
178{
179	char buf[256];
180
181	(void)snprintf(buf, sizeof(buf), "O%s\n%d\n", tape, mode);
182	rmtstate = TS_OPEN;
183	return (rmtcall(tape, buf));
184}
185
186void
187rmtclose(void)
188{
189
190	if (rmtstate != TS_OPEN)
191		return;
192	rmtcall("close", "C\n");
193	rmtstate = TS_CLOSED;
194}
195
196int
197rmtread(char *buf, int count)
198{
199	char line[30];
200	int n, i, cc;
201
202	(void)snprintf(line, sizeof(line), "R%d\n", count);
203	n = rmtcall("read", line);
204	if (n < 0) {
205		errno = n;
206		return (-1);
207	}
208	for (i = 0; i < n; i += cc) {
209		cc = read(rmtape, buf+i, n - i);
210		if (cc <= 0) {
211			rmtconnaborted(0);
212		}
213	}
214	return (n);
215}
216
217int
218rmtwrite(char *buf, int count)
219{
220	char line[30];
221
222	(void)snprintf(line, sizeof(line), "W%d\n", count);
223	write(rmtape, line, strlen(line));
224	write(rmtape, buf, count);
225	return (rmtreply("write"));
226}
227
228int
229rmtseek(int offset, int pos)
230{
231	char line[80];
232
233	(void)snprintf(line, sizeof(line), "L%d\n%d\n", offset, pos);
234	return (rmtcall("seek", line));
235}
236
237int
238rmtioctl(int cmd, int count)
239{
240	char buf[256];
241
242	if (count < 0)
243		return (-1);
244	(void)snprintf(buf, sizeof(buf), "I%d\n%d\n", cmd, count);
245	return (rmtcall("ioctl", buf));
246}
247
248static int
249rmtcall(char *cmd, char *buf)
250{
251
252	if (write(rmtape, buf, strlen(buf)) != strlen(buf))
253		rmtconnaborted(0);
254	return (rmtreply(cmd));
255}
256
257static int
258rmtreply(char *cmd)
259{
260	char *cp;
261	char code[30], emsg[BUFSIZ];
262
263	rmtgets(code, sizeof(code));
264	if (*code == 'E' || *code == 'F') {
265		rmtgets(emsg, sizeof(emsg));
266		msg("%s: %s", cmd, emsg);
267		errno = atoi(&code[1]);
268		if (*code == 'F')
269			rmtstate = TS_CLOSED;
270		return (-1);
271	}
272	if (*code != 'A') {
273		/* Kill trailing newline */
274		cp = code + strlen(code);
275		if (cp > code && *--cp == '\n')
276			*cp = '\0';
277
278		msg("Protocol to remote tape server botched (code \"%s\").\n",
279		    code);
280		rmtconnaborted(0);
281	}
282	return (atoi(code + 1));
283}
284
285int
286rmtgetb(void)
287{
288	char c;
289
290	if (read(rmtape, &c, 1) != 1)
291		rmtconnaborted(0);
292	return (c);
293}
294
295/* Get a line (guaranteed to have a trailing newline). */
296void
297rmtgets(char *line, int len)
298{
299	char *cp = line;
300
301	while (len > 1) {
302		*cp = rmtgetb();
303		if (*cp == '\n') {
304			cp[1] = '\0';
305			return;
306		}
307		cp++;
308		len--;
309	}
310	*cp = '\0';
311	msg("Protocol to remote tape server botched.\n");
312	msg("(rmtgets got \"%s\").\n", line);
313	rmtconnaborted(0);
314}
315