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