1/*
2 *  sock.c
3 *
4 *  Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke
5 *  Copyright (C) 1997 by Volker Lendecke
6 *
7 *  Please add a note about your changes to smbfs in the ChangeLog file.
8 */
9
10#include <linux/fs.h>
11#include <linux/time.h>
12#include <linux/errno.h>
13#include <linux/socket.h>
14#include <linux/fcntl.h>
15#include <linux/file.h>
16#include <linux/in.h>
17#include <linux/net.h>
18#include <linux/mm.h>
19#include <linux/netdevice.h>
20#include <linux/workqueue.h>
21#include <net/scm.h>
22#include <net/tcp_states.h>
23#include <net/ip.h>
24
25#include <linux/smb_fs.h>
26#include <linux/smb.h>
27#include <linux/smbno.h>
28
29#include <asm/uaccess.h>
30#include <asm/ioctls.h>
31
32#include "smb_debug.h"
33#include "proto.h"
34#include "request.h"
35
36
37static int
38_recvfrom(struct socket *socket, unsigned char *ubuf, int size, unsigned flags)
39{
40	struct kvec iov = {ubuf, size};
41	struct msghdr msg = {.msg_flags = flags};
42	msg.msg_flags |= MSG_DONTWAIT | MSG_NOSIGNAL;
43	return kernel_recvmsg(socket, &msg, &iov, 1, size, msg.msg_flags);
44}
45
46/*
47 * Return the server this socket belongs to
48 */
49static struct smb_sb_info *
50server_from_socket(struct socket *socket)
51{
52	return socket->sk->sk_user_data;
53}
54
55/*
56 * Called when there is data on the socket.
57 */
58void
59smb_data_ready(struct sock *sk, int len)
60{
61	struct smb_sb_info *server = server_from_socket(sk->sk_socket);
62	void (*data_ready)(struct sock *, int) = server->data_ready;
63
64	data_ready(sk, len);
65	VERBOSE("(%p, %d)\n", sk, len);
66	smbiod_wake_up();
67}
68
69int
70smb_valid_socket(struct inode * inode)
71{
72	return (inode && S_ISSOCK(inode->i_mode) &&
73		SOCKET_I(inode)->type == SOCK_STREAM);
74}
75
76static struct socket *
77server_sock(struct smb_sb_info *server)
78{
79	struct file *file;
80
81	if (server && (file = server->sock_file))
82	{
83#ifdef SMBFS_PARANOIA
84		if (!smb_valid_socket(file->f_path.dentry->d_inode))
85			PARANOIA("bad socket!\n");
86#endif
87		return SOCKET_I(file->f_path.dentry->d_inode);
88	}
89	return NULL;
90}
91
92void
93smb_close_socket(struct smb_sb_info *server)
94{
95	struct file * file = server->sock_file;
96
97	if (file) {
98		struct socket *sock = server_sock(server);
99
100		VERBOSE("closing socket %p\n", sock);
101		sock->sk->sk_data_ready = server->data_ready;
102		server->sock_file = NULL;
103		fput(file);
104	}
105}
106
107static int
108smb_get_length(struct socket *socket, unsigned char *header)
109{
110	int result;
111
112	result = _recvfrom(socket, header, 4, MSG_PEEK);
113	if (result == -EAGAIN)
114		return -ENODATA;
115	if (result < 0) {
116		PARANOIA("recv error = %d\n", -result);
117		return result;
118	}
119	if (result < 4)
120		return -ENODATA;
121
122	switch (header[0]) {
123	case 0x00:
124	case 0x82:
125		break;
126
127	case 0x85:
128		DEBUG1("Got SESSION KEEP ALIVE\n");
129		_recvfrom(socket, header, 4, 0);	/* read away */
130		return -ENODATA;
131
132	default:
133		PARANOIA("Invalid NBT packet, code=%x\n", header[0]);
134		return -EIO;
135	}
136
137	/* The length in the RFC NB header is the raw data length */
138	return smb_len(header);
139}
140
141int
142smb_recv_available(struct smb_sb_info *server)
143{
144	mm_segment_t oldfs;
145	int avail, err;
146	struct socket *sock = server_sock(server);
147
148	oldfs = get_fs();
149	set_fs(get_ds());
150	err = sock->ops->ioctl(sock, SIOCINQ, (unsigned long) &avail);
151	set_fs(oldfs);
152	return (err >= 0) ? avail : err;
153}
154
155/*
156 * Adjust the kvec to move on 'n' bytes (from nfs/sunrpc)
157 */
158static int
159smb_move_iov(struct kvec **data, size_t *num, struct kvec *vec, unsigned amount)
160{
161	struct kvec *iv = *data;
162	int i;
163	int len;
164
165	/*
166	 *	Eat any sent kvecs
167	 */
168	while (iv->iov_len <= amount) {
169		amount -= iv->iov_len;
170		iv++;
171		(*num)--;
172	}
173
174	/*
175	 *	And chew down the partial one
176	 */
177	vec[0].iov_len = iv->iov_len-amount;
178	vec[0].iov_base =((unsigned char *)iv->iov_base)+amount;
179	iv++;
180
181	len = vec[0].iov_len;
182
183	/*
184	 *	And copy any others
185	 */
186	for (i = 1; i < *num; i++) {
187		vec[i] = *iv++;
188		len += vec[i].iov_len;
189	}
190
191	*data = vec;
192	return len;
193}
194
195/*
196 * smb_receive_header
197 * Only called by the smbiod thread.
198 */
199int
200smb_receive_header(struct smb_sb_info *server)
201{
202	struct socket *sock;
203	int result = 0;
204	unsigned char peek_buf[4];
205
206	result = -EIO;
207	sock = server_sock(server);
208	if (!sock)
209		goto out;
210	if (sock->sk->sk_state != TCP_ESTABLISHED)
211		goto out;
212
213	if (!server->smb_read) {
214		result = smb_get_length(sock, peek_buf);
215		if (result < 0) {
216			if (result == -ENODATA)
217				result = 0;
218			goto out;
219		}
220		server->smb_len = result + 4;
221
222		if (server->smb_len < SMB_HEADER_LEN) {
223			PARANOIA("short packet: %d\n", result);
224			server->rstate = SMB_RECV_DROP;
225			result = -EIO;
226			goto out;
227		}
228		if (server->smb_len > SMB_MAX_PACKET_SIZE) {
229			PARANOIA("long packet: %d\n", result);
230			server->rstate = SMB_RECV_DROP;
231			result = -EIO;
232			goto out;
233		}
234	}
235
236	result = _recvfrom(sock, server->header + server->smb_read,
237			   SMB_HEADER_LEN - server->smb_read, 0);
238	VERBOSE("_recvfrom: %d\n", result);
239	if (result < 0) {
240		VERBOSE("receive error: %d\n", result);
241		goto out;
242	}
243	server->smb_read += result;
244
245	if (server->smb_read == SMB_HEADER_LEN)
246		server->rstate = SMB_RECV_HCOMPLETE;
247out:
248	return result;
249}
250
251static char drop_buffer[PAGE_SIZE];
252
253int
254smb_receive_drop(struct smb_sb_info *server)
255{
256	struct socket *sock;
257	unsigned int flags;
258	struct kvec iov;
259	struct msghdr msg;
260	int rlen = smb_len(server->header) - server->smb_read + 4;
261	int result = -EIO;
262
263	if (rlen > PAGE_SIZE)
264		rlen = PAGE_SIZE;
265
266	sock = server_sock(server);
267	if (!sock)
268		goto out;
269	if (sock->sk->sk_state != TCP_ESTABLISHED)
270		goto out;
271
272	flags = MSG_DONTWAIT | MSG_NOSIGNAL;
273	iov.iov_base = drop_buffer;
274	iov.iov_len = PAGE_SIZE;
275	msg.msg_flags = flags;
276	msg.msg_name = NULL;
277	msg.msg_namelen = 0;
278	msg.msg_control = NULL;
279
280	result = kernel_recvmsg(sock, &msg, &iov, 1, rlen, flags);
281
282	VERBOSE("read: %d\n", result);
283	if (result < 0) {
284		VERBOSE("receive error: %d\n", result);
285		goto out;
286	}
287	server->smb_read += result;
288
289	if (server->smb_read >= server->smb_len)
290		server->rstate = SMB_RECV_END;
291
292out:
293	return result;
294}
295
296/*
297 * smb_receive
298 * Only called by the smbiod thread.
299 */
300int
301smb_receive(struct smb_sb_info *server, struct smb_request *req)
302{
303	struct socket *sock;
304	unsigned int flags;
305	struct kvec iov[4];
306	struct kvec *p = req->rq_iov;
307	size_t num = req->rq_iovlen;
308	struct msghdr msg;
309	int rlen;
310	int result = -EIO;
311
312	sock = server_sock(server);
313	if (!sock)
314		goto out;
315	if (sock->sk->sk_state != TCP_ESTABLISHED)
316		goto out;
317
318	flags = MSG_DONTWAIT | MSG_NOSIGNAL;
319	msg.msg_flags = flags;
320	msg.msg_name = NULL;
321	msg.msg_namelen = 0;
322	msg.msg_control = NULL;
323
324	/* Dont repeat bytes and count available bufferspace */
325	rlen = min_t(int, smb_move_iov(&p, &num, iov, req->rq_bytes_recvd),
326			(req->rq_rlen - req->rq_bytes_recvd));
327
328	result = kernel_recvmsg(sock, &msg, p, num, rlen, flags);
329
330	VERBOSE("read: %d\n", result);
331	if (result < 0) {
332		VERBOSE("receive error: %d\n", result);
333		goto out;
334	}
335	req->rq_bytes_recvd += result;
336	server->smb_read += result;
337
338out:
339	return result;
340}
341
342/*
343 * Try to send a SMB request. This may return after sending only parts of the
344 * request. SMB_REQ_TRANSMITTED will be set if a request was fully sent.
345 *
346 * Parts of this was taken from xprt_sendmsg from net/sunrpc/xprt.c
347 */
348int
349smb_send_request(struct smb_request *req)
350{
351	struct smb_sb_info *server = req->rq_server;
352	struct socket *sock;
353	struct msghdr msg = {.msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT};
354        int slen = req->rq_slen - req->rq_bytes_sent;
355	int result = -EIO;
356	struct kvec iov[4];
357	struct kvec *p = req->rq_iov;
358	size_t num = req->rq_iovlen;
359
360	sock = server_sock(server);
361	if (!sock)
362		goto out;
363	if (sock->sk->sk_state != TCP_ESTABLISHED)
364		goto out;
365
366	/* Dont repeat bytes */
367	if (req->rq_bytes_sent)
368		smb_move_iov(&p, &num, iov, req->rq_bytes_sent);
369
370	result = kernel_sendmsg(sock, &msg, p, num, slen);
371
372	if (result >= 0) {
373		req->rq_bytes_sent += result;
374		if (req->rq_bytes_sent >= req->rq_slen)
375			req->rq_flags |= SMB_REQ_TRANSMITTED;
376	}
377out:
378	return result;
379}
380