xref: /aosp_15_r20/external/ublksrv/nbd/nbd-client.c (revision 94c4a1e103eb1715230460aab379dff275992c20)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 /*
4  * Open connection for network block device
5  *
6  * Copyright 1997,1998 Pavel Machek, distribute under GPL
7  *  <[email protected]>
8  * Copyright (c) 2002 - 2011 Wouter Verhelst <[email protected]>
9  *
10  * Version 1.0 - 64bit issues should be fixed, now
11  * Version 1.1 - added bs (blocksize) option (Alexey Guzeev, [email protected])
12  * Version 1.2 - I added new option '-d' to send the disconnect request
13  * Version 2.0 - Version synchronised with server
14  * Version 2.1 - Check for disconnection before INIT_PASSWD is received
15  * 	to make errormsg a bit more helpful in case the server can't
16  * 	open the exported file.
17  * 16/03/2010 - Add IPv6 support.
18  * 	Kitt Tientanopajai <[email protected]>
19  *	Neutron Soutmun <[email protected]>
20  *	Suriya Soutmun <[email protected]>
21  */
22 
23 #include <config.h>
24 #include <sys/ioctl.h>
25 #include <sys/socket.h>
26 #include <sys/un.h>
27 #include <sys/types.h>
28 #include <unistd.h>
29 #include <netinet/tcp.h>
30 #include <netinet/in.h>
31 #include <netdb.h>
32 #include <inttypes.h>
33 #include <stdio.h>
34 #include <fcntl.h>
35 #include <syslog.h>
36 #include <stdlib.h>
37 #include <sys/mount.h>
38 #include <sys/mman.h>
39 #include <signal.h>
40 #include <errno.h>
41 #include <getopt.h>
42 #include <stdarg.h>
43 #include <stdbool.h>
44 #include <time.h>
45 
46 #include <linux/ioctl.h>
47 
48 #define MY_NAME "ublk_nbd"
49 #include "cliserv.h"
50 
51 #if HAVE_GNUTLS && !defined(NOTLS)
52 #include "crypto-gnutls.h"
53 #endif
54 
55 #define NBDC_DO_LIST 1
56 
opennet(const char * name,const char * portstr,int sdp)57 int opennet(const char *name, const char* portstr, int sdp) {
58 	int sock;
59 	struct addrinfo hints;
60 	struct addrinfo *ai = NULL;
61 	struct addrinfo *rp = NULL;
62 	int e;
63 
64 	memset(&hints,'\0',sizeof(hints));
65 	hints.ai_family = AF_UNSPEC;
66 	hints.ai_socktype = SOCK_STREAM;
67 	hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
68 	hints.ai_protocol = IPPROTO_TCP;
69 
70 	e = getaddrinfo(name, portstr, &hints, &ai);
71 
72 	if(e != 0) {
73 		fprintf(stderr, "getaddrinfo failed: %s\n", gai_strerror(e));
74 		freeaddrinfo(ai);
75 		return -1;
76 	}
77 
78 	if(sdp) {
79 #ifdef WITH_SDP
80 		if (ai->ai_family == AF_INET)
81 			ai->ai_family = AF_INET_SDP;
82 		else (ai->ai_family == AF_INET6)
83 			ai->ai_family = AF_INET6_SDP;
84 #else
85 		err("Can't do SDP: I was not compiled with SDP support!");
86 #endif
87 	}
88 
89 	for(rp = ai; rp != NULL; rp = rp->ai_next) {
90 		sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
91 
92 		if(sock == -1)
93 			continue;	/* error */
94 
95 		if(connect(sock, rp->ai_addr, rp->ai_addrlen) != -1)
96 			break;		/* success */
97 
98 		close(sock);
99 	}
100 
101 	if (rp == NULL) {
102 		err_nonfatal("Socket failed: %m");
103 		sock = -1;
104 		goto err;
105 	}
106 
107 	setmysockopt(sock);
108 err:
109 	freeaddrinfo(ai);
110 	return sock;
111 }
112 
openunix(const char * path)113 int openunix(const char *path) {
114 	int sock;
115 	struct sockaddr_un un_addr;
116 	memset(&un_addr, 0, sizeof(un_addr));
117 
118 	un_addr.sun_family = AF_UNIX;
119 	if (strnlen(path, sizeof(un_addr.sun_path)) == sizeof(un_addr.sun_path)) {
120 		err_nonfatal("UNIX socket path too long");
121 		return -1;
122 	}
123 
124 	strncpy(un_addr.sun_path, path, sizeof(un_addr.sun_path) - 1);
125 
126 	if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
127 		err_nonfatal("SOCKET failed");
128 		return -1;
129 	};
130 
131 	if (connect(sock, &un_addr, sizeof(un_addr)) == -1) {
132 		err_nonfatal("CONNECT failed");
133 		close(sock);
134 		return -1;
135 	}
136 	return sock;
137 }
138 
send_request(int sock,uint32_t opt,ssize_t datasize,void * data)139 static void send_request(int sock, uint32_t opt, ssize_t datasize, void* data) {
140 	struct {
141 		uint64_t magic;
142 		uint32_t opt;
143 		uint32_t datasize;
144 	} __attribute__((packed)) header = {
145 		ntohll(opts_magic),
146 		ntohl(opt),
147 		ntohl(datasize),
148 	};
149 	if(datasize < 0) {
150 		datasize = strlen((char*)data);
151 		header.datasize = htonl(datasize);
152 	}
153 	writeit(sock, &header, sizeof(header));
154 	if(data != NULL) {
155 		writeit(sock, data, datasize);
156 	}
157 }
158 
send_info_request(int sock,uint32_t opt,int n_reqs,uint16_t * reqs,char * name)159 static void send_info_request(int sock, uint32_t opt, int n_reqs,
160 		uint16_t* reqs, char* name) {
161 	uint16_t rlen = htons(n_reqs);
162 	uint32_t nlen = htonl(strlen(name));
163 
164 	send_request(sock, opt, sizeof(uint32_t) + strlen(name) + sizeof(uint16_t) + n_reqs * sizeof(uint16_t), NULL);
165 	writeit(sock, &nlen, sizeof(nlen));
166 	writeit(sock, name, strlen(name));
167 	writeit(sock, &rlen, sizeof(rlen));
168 	if(n_reqs > 0) {
169 		writeit(sock, reqs, n_reqs * sizeof(uint16_t));
170 	}
171 }
172 
173 struct reply {
174 	uint64_t magic;
175 	uint32_t opt;
176 	uint32_t reply_type;
177 	uint32_t datasize;
178 	char data[];
179 } __attribute__((packed));
180 
read_reply(int sock)181 static struct reply* read_reply(int sock) {
182 	struct reply *retval = (struct reply *)malloc(sizeof(struct reply));
183 	readit(sock, retval, sizeof(*retval));
184 	retval->magic = ntohll(retval->magic);
185 	retval->opt = ntohl(retval->opt);
186 	retval->reply_type = ntohl(retval->reply_type);
187 	retval->datasize = ntohl(retval->datasize);
188 	if (retval->magic != rep_magic) {
189 		fprintf(stderr, "E: received invalid negotiation magic %" PRIu64 " (expected %" PRIu64 ")", retval->magic, rep_magic);
190 		exit(EXIT_FAILURE);
191 	}
192 	if (retval->datasize > 0) {
193 		retval = (struct reply *)realloc(retval, sizeof(struct reply) + retval->datasize);
194 		readit(sock, &(retval->data), retval->datasize);
195 	}
196 	return retval;
197 }
198 
ask_list(int sock)199 static void ask_list(int sock) {
200 	uint32_t opt_server;
201 	uint32_t len;
202 	uint32_t lenn;
203 	uint32_t reptype;
204 	uint64_t magic;
205 	int rlen;
206 #define BUF_SIZE 1024
207 	char buf[BUF_SIZE];
208 
209 	send_request(sock, NBD_OPT_LIST, 0, NULL);
210 	/* newline, move away from the "Negotiation:" line */
211 	printf("\n");
212 	do {
213 		memset(buf, 0, 1024);
214 		if(read(sock, &magic, sizeof(magic)) < 0) {
215 			err("Reading magic from server: %m");
216 		}
217 		if(read(sock, &opt_server, sizeof(opt_server)) < 0) {
218 			err("Reading option: %m");
219 		}
220 		if(read(sock, &reptype, sizeof(reptype)) <0) {
221 			err("Reading reply from server: %m");
222 		}
223 		if(read(sock, &len, sizeof(len)) < 0) {
224 			err("Reading length from server: %m");
225 		}
226 		magic=ntohll(magic);
227 		len=ntohl(len);
228 		reptype=ntohl(reptype);
229 		if(magic != rep_magic) {
230 			err("Not enough magic from server");
231 		}
232 		if(reptype & NBD_REP_FLAG_ERROR) {
233 			switch(reptype) {
234 				case NBD_REP_ERR_POLICY:
235 					fprintf(stderr, "\nE: listing not allowed by server.\n");
236 					break;
237 				default:
238 					fprintf(stderr, "\nE: unexpected error from server.\n");
239 					break;
240 			}
241 			if(len > 0 && len < BUF_SIZE) {
242 				if((rlen=read(sock, buf, len)) < 0) {
243 					fprintf(stderr, "\nE: could not read error message from server\n");
244 				} else {
245 					buf[rlen] = '\0';
246 					fprintf(stderr, "Server said: %s\n", buf);
247 				}
248 			}
249 			exit(EXIT_FAILURE);
250 		} else {
251 			if(reptype != NBD_REP_ACK) {
252 				if(reptype != NBD_REP_SERVER) {
253 					err("Server sent us a reply we don't understand!");
254 				}
255 				if(read(sock, &lenn, sizeof(lenn)) < 0) {
256 					fprintf(stderr, "\nE: could not read export name length from server\n");
257 					exit(EXIT_FAILURE);
258 				}
259 				lenn=ntohl(lenn);
260 				if (lenn >= BUF_SIZE) {
261 					fprintf(stderr, "\nE: export name on server too long\n");
262 					exit(EXIT_FAILURE);
263 				}
264 				if(read(sock, buf, lenn) < 0) {
265 					fprintf(stderr, "\nE: could not read export name from server\n");
266 					exit(EXIT_FAILURE);
267 				}
268 				buf[lenn] = 0;
269 				printf("%s", buf);
270 				len -= lenn;
271 				len -= sizeof(lenn);
272 				if(len > 0) {
273 					if(read(sock, buf, len) < 0) {
274 						fprintf(stderr, "\nE: could not read export description from server\n");
275 						exit(EXIT_FAILURE);
276 					}
277 					buf[len] = 0;
278 					printf(": %s\n", buf);
279 				} else {
280 					printf("\n");
281 				}
282 			}
283 		}
284 	} while(reptype != NBD_REP_ACK);
285 	send_request(sock, NBD_OPT_ABORT, 0, NULL);
286 }
287 
parse_sizes(char * buf,uint64_t * size,uint16_t * flags)288 static void parse_sizes(char *buf, uint64_t *size, uint16_t *flags) {
289 	memcpy(size, buf, sizeof(*size));
290 	*size = ntohll(*size);
291 	buf += sizeof(*size);
292 	memcpy(flags, buf, sizeof(*flags));
293 	*flags = ntohs(*flags);
294 
295 	if ((*size>>12) > (uint64_t)~0UL) {
296 		printf("size = %luMB", (unsigned long)(*size>>20));
297 		err("Exported device is too big for me. Get 64-bit machine :-(\n");
298 	} else {
299 		printf("size = %luMB", (unsigned long)(*size>>20));
300 	}
301 	printf("\n");
302 }
303 
send_opt_exportname(int sock,u64 * rsize64,uint16_t * flags,bool can_opt_go,char * name,uint16_t global_flags)304 static void send_opt_exportname(int sock, u64 *rsize64, uint16_t *flags,
305 		bool can_opt_go, char* name, uint16_t global_flags) {
306 	send_request(sock, NBD_OPT_EXPORT_NAME, -1, name);
307 	char b[sizeof(*flags) + sizeof(*rsize64)];
308 	if(readit(sock, b, sizeof(b)) < 0 && can_opt_go) {
309 		err("E: server does not support NBD_OPT_GO and dropped connection after sending NBD_OPT_EXPORT_NAME. Try -g.");
310 	}
311 	parse_sizes(b, rsize64, flags);
312 	if(!(global_flags & NBD_FLAG_NO_ZEROES)) {
313 		char buf[125];
314 		readit(sock, buf, 124);
315 	}
316 }
317 
318 
negotiate(int * sockp,u64 * rsize64,uint16_t * flags,char * name,uint32_t needed_flags,uint32_t client_flags,uint32_t do_opts,char * certfile,char * keyfile,char * cacertfile,char * tlshostname,bool tls,bool can_opt_go)319 void negotiate(int *sockp, u64 *rsize64, uint16_t *flags, char* name,
320 		uint32_t needed_flags, uint32_t client_flags, uint32_t do_opts,
321 		char *certfile, char *keyfile, char *cacertfile,
322 		char *tlshostname, bool tls, bool can_opt_go) {
323 	u64 magic;
324 	uint16_t tmp;
325 	uint16_t global_flags;
326 	char buf[256] = "\0\0\0\0\0\0\0\0\0";
327 	int sock = *sockp;
328 
329 	printf("Negotiation: ");
330 	readit(sock, buf, 8);
331 	if (strcmp(buf, INIT_PASSWD))
332 		err("INIT_PASSWD bad");
333 	printf(".");
334 	readit(sock, &magic, sizeof(magic));
335 	magic = ntohll(magic);
336 	if (magic != opts_magic) {
337 		if(magic == cliserv_magic) {
338 			err("It looks like you're trying to connect to an oldstyle server. This is no longer supported since nbd 3.10.");
339 		}
340 	}
341 	printf(".");
342 	readit(sock, &tmp, sizeof(uint16_t));
343 	global_flags = ntohs(tmp);
344 	if((needed_flags & global_flags) != needed_flags) {
345 		/* There's currently really only one reason why this
346 		 * check could possibly fail, but we may need to change
347 		 * this error message in the future... */
348 		fprintf(stderr, "\nE: Server does not support listing exports\n");
349 		exit(EXIT_FAILURE);
350 	}
351 
352 	if (global_flags & NBD_FLAG_NO_ZEROES) {
353 		client_flags |= NBD_FLAG_C_NO_ZEROES;
354 	}
355 	client_flags = htonl(client_flags);
356 	if (write(sock, &client_flags, sizeof(client_flags)) < 0)
357 		err("Failed/2.1: %m");
358 
359 #if HAVE_GNUTLS && !defined(NOTLS)
360         /* TLS */
361         if (tls) {
362 		int plainfd[2]; // [0] is used by the proxy, [1] is used by NBD
363 		tlssession_t *s = NULL;
364 		int ret;
365 		uint32_t tmp32;
366 		uint64_t tmp64;
367 
368 		send_request(sock, NBD_OPT_STARTTLS, 0, NULL);
369 
370 		if (read(sock, &tmp64, sizeof(tmp64)) < 0)
371 			err("Could not read cliserv_magic: %m");
372 		tmp64 = ntohll(tmp64);
373 		if (tmp64 != NBD_OPT_REPLY_MAGIC) {
374 			err("reply magic does not match");
375 		}
376 		if (read(sock, &tmp32, sizeof(tmp32)) < 0)
377 			err("Could not read option type: %m");
378 		tmp32 = ntohl(tmp32);
379 		if (tmp32 != NBD_OPT_STARTTLS)
380 			err("Reply to wrong option");
381 		if (read(sock, &tmp32, sizeof(tmp32)) < 0)
382 			err("Could not read option reply type: %m");
383 		tmp32 = ntohl(tmp32);
384 		if (tmp32 != NBD_REP_ACK) {
385 			err("Option reply type != NBD_REP_ACK");
386 		}
387 		if (read(sock, &tmp32, sizeof(tmp32)) < 0) err(
388 			"Could not read option data length: %m");
389 		tmp32 = ntohl(tmp32);
390 		if (tmp32 != 0) {
391 			err("Option reply data length != 0");
392 		}
393 		s = tlssession_new(0,
394 				   keyfile,
395 				   certfile,
396 				   cacertfile,
397 				   tlshostname,
398 				   !cacertfile || !tlshostname, // insecure flag
399 #ifdef DODBG
400 				   1, // debug
401 #else
402 				   0, // debug
403 #endif
404 				   NULL, // quitfn
405 				   NULL, // erroutfn
406 				   NULL // opaque
407 			);
408 		if (!s)
409 			err("Cannot establish TLS session");
410 
411 		if (socketpair(AF_UNIX, SOCK_STREAM, 0, plainfd) < 0)
412 			err("Cannot get socket pair");
413 
414 		if (set_nonblocking(plainfd[0], 0) <0 ||
415 		    set_nonblocking(plainfd[1], 0) <0 ||
416 		    set_nonblocking(sock, 0) <0) {
417 			close(plainfd[0]);
418 			close(plainfd[1]);
419 			err("Cannot set socket options");
420 		}
421 
422 		ret = fork();
423 		if (ret < 0)
424 			err("Could not fork");
425 		else if (ret == 0) {
426 			// we are the child
427 			if (daemon(0, 0) < 0) {
428 				/* no one will see this */
429 				fprintf(stderr, "Can't detach from the terminal");
430 				exit(1);
431 			}
432 			signal (SIGPIPE, SIG_IGN);
433 			close(plainfd[1]);
434 			tlssession_mainloop(sock, plainfd[0], s);
435 			close(sock);
436 			close(plainfd[0]);
437 			exit(0);
438 		}
439 		close(plainfd[0]);
440 		close(sock);
441 		sock = plainfd[1]; /* use the decrypted FD from now on */
442 		*sockp = sock;
443 	}
444 #else
445 	if (keyfile) {
446 		err("TLS requested but support not compiled in");
447 	}
448 #endif
449 
450 	if(do_opts & NBDC_DO_LIST) {
451 		ask_list(sock);
452 		exit(EXIT_SUCCESS);
453 	}
454 
455 	struct reply *rep = NULL;
456 
457 	if(!can_opt_go) {
458 		send_opt_exportname(sock, rsize64, flags, can_opt_go, name, global_flags);
459 		return;
460 	}
461 
462 	send_info_request(sock, NBD_OPT_GO, 0, NULL, name);
463 
464 	do {
465 		if(rep != NULL) free(rep);
466 		rep = read_reply(sock);
467 		if(rep && (rep->reply_type & NBD_REP_FLAG_ERROR)) {
468 			switch(rep->reply_type) {
469 				case NBD_REP_ERR_UNSUP:
470 					/* server doesn't support NBD_OPT_GO or NBD_OPT_INFO,
471 					 * fall back to NBD_OPT_EXPORT_NAME */
472 					send_opt_exportname(sock, rsize64, flags, can_opt_go, name, global_flags);
473 					free(rep);
474 					return;
475 				case NBD_REP_ERR_POLICY:
476 					if(rep->datasize > 0) {
477 						char errstr[1024];
478 						snprintf(errstr, sizeof errstr, "Connection not allowed by server policy. Server said: %s", rep->data);
479 						err(errstr);
480 					} else {
481 						err("Connection not allowed by server policy.");
482 					}
483 					free(rep);
484 					exit(EXIT_FAILURE);
485 				default:
486 					if(rep->datasize > 0) {
487 						char errstr[1024];
488 						snprintf(errstr, sizeof errstr, "Unknown error returned by server. Server said: %s", rep->data);
489 						err(errstr);
490 					} else {
491 						err("Unknown error returned by server.");
492 					}
493 					free(rep);
494 					exit(EXIT_FAILURE);
495 			}
496 		}
497 		uint16_t info_type;
498 		switch(rep->reply_type) {
499 			case NBD_REP_INFO:
500 				memcpy(&info_type, rep->data, 2);
501 				info_type = htons(info_type);
502 				switch(info_type) {
503 					case NBD_INFO_EXPORT:
504 						parse_sizes(rep->data + 2, rsize64, flags);
505 						break;
506 					default:
507 						// ignore these, don't need them
508 						break;
509 				}
510 				break;
511 			case NBD_REP_ACK:
512 				break;
513 			default:
514 				err_nonfatal("Unknown reply to NBD_OPT_GO received, ignoring");
515 		}
516 	} while(rep->reply_type != NBD_REP_ACK);
517 	free(rep);
518 }
519