ipv4: fix redirect handling
[linux-2.6.git] / net / tipc / socket.c
index e9fc5df..adb2eff 100644 (file)
@@ -2,7 +2,7 @@
  * net/tipc/socket.c: TIPC socket API
  *
  * Copyright (c) 2001-2007, Ericsson AB
- * Copyright (c) 2004-2008, Wind River Systems
+ * Copyright (c) 2004-2008, 2010-2011, Wind River Systems
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -36,9 +36,6 @@
 
 #include <net/sock.h>
 
-#include <linux/tipc.h>
-#include <linux/tipc_config.h>
-
 #include "core.h"
 #include "port.h"
 
@@ -58,6 +55,9 @@ struct tipc_sock {
 #define tipc_sk(sk) ((struct tipc_sock *)(sk))
 #define tipc_sk_port(sk) ((struct tipc_port *)(tipc_sk(sk)->p))
 
+#define tipc_rx_ready(sock) (!skb_queue_empty(&sock->sk->sk_receive_queue) || \
+                       (sock->state == SS_DISCONNECTING))
+
 static int backlog_rcv(struct sock *sk, struct sk_buff *skb);
 static u32 dispatch(struct tipc_port *tport, struct sk_buff *buf);
 static void wakeupdispatch(struct tipc_port *tport);
@@ -68,7 +68,7 @@ static const struct proto_ops msg_ops;
 
 static struct proto tipc_proto;
 
-static int sockets_enabled = 0;
+static int sockets_enabled;
 
 static atomic_t tipc_queue_size = ATOMIC_INIT(0);
 
@@ -241,7 +241,6 @@ static int tipc_create(struct net *net, struct socket *sock, int protocol,
                        tipc_set_portunreliable(tp_ptr->ref, 1);
        }
 
-       atomic_inc(&tipc_user_count);
        return 0;
 }
 
@@ -290,7 +289,7 @@ static int release(struct socket *sock)
                if (buf == NULL)
                        break;
                atomic_dec(&tipc_queue_size);
-               if (TIPC_SKB_CB(buf)->handle != msg_data(buf_msg(buf)))
+               if (TIPC_SKB_CB(buf)->handle != 0)
                        buf_discard(buf);
                else {
                        if ((sock->state == SS_CONNECTING) ||
@@ -321,7 +320,6 @@ static int release(struct socket *sock)
        sock_put(sk);
        sock->sk = NULL;
 
-       atomic_dec(&tipc_user_count);
        return res;
 }
 
@@ -495,6 +493,8 @@ static int dest_name_check(struct sockaddr_tipc *dest, struct msghdr *m)
        if (likely(dest->addr.name.name.type != TIPC_CFG_SRV))
                return -EACCES;
 
+       if (!m->msg_iovlen || (m->msg_iov[0].iov_len < sizeof(hdr)))
+               return -EMSGSIZE;
        if (copy_from_user(&hdr, m->msg_iov[0].iov_base, sizeof(hdr)))
                return -EFAULT;
        if ((ntohs(hdr.tcm_type) & 0xC000) && (!capable(CAP_NET_ADMIN)))
@@ -532,6 +532,9 @@ static int send_msg(struct kiocb *iocb, struct socket *sock,
        if (unlikely((m->msg_namelen < sizeof(*dest)) ||
                     (dest->family != AF_TIPC)))
                return -EINVAL;
+       if ((total_len > TIPC_MAX_USER_MSG_SIZE) ||
+           (m->msg_iovlen > (unsigned)INT_MAX))
+               return -EMSGSIZE;
 
        if (iocb)
                lock_sock(sk);
@@ -563,34 +566,38 @@ static int send_msg(struct kiocb *iocb, struct socket *sock,
 
        do {
                if (dest->addrtype == TIPC_ADDR_NAME) {
-                       if ((res = dest_name_check(dest, m)))
+                       res = dest_name_check(dest, m);
+                       if (res)
                                break;
                        res = tipc_send2name(tport->ref,
                                             &dest->addr.name.name,
                                             dest->addr.name.domain,
                                             m->msg_iovlen,
-                                            m->msg_iov);
+                                            m->msg_iov,
+                                            total_len);
                } else if (dest->addrtype == TIPC_ADDR_ID) {
                        res = tipc_send2port(tport->ref,
                                             &dest->addr.id,
                                             m->msg_iovlen,
-                                            m->msg_iov);
+                                            m->msg_iov,
+                                            total_len);
                } else if (dest->addrtype == TIPC_ADDR_MCAST) {
                        if (needs_conn) {
                                res = -EOPNOTSUPP;
                                break;
                        }
-                       if ((res = dest_name_check(dest, m)))
+                       res = dest_name_check(dest, m);
+                       if (res)
                                break;
                        res = tipc_multicast(tport->ref,
                                             &dest->addr.nameseq,
                                             m->msg_iovlen,
-                                            m->msg_iov);
+                                            m->msg_iov,
+                                            total_len);
                }
                if (likely(res != -ELINKCONG)) {
-                       if (needs_conn && (res >= 0)) {
+                       if (needs_conn && (res >= 0))
                                sock->state = SS_CONNECTING;
-                       }
                        break;
                }
                if (m->msg_flags & MSG_DONTWAIT) {
@@ -636,6 +643,10 @@ static int send_packet(struct kiocb *iocb, struct socket *sock,
        if (unlikely(dest))
                return send_msg(iocb, sock, m, total_len);
 
+       if ((total_len > TIPC_MAX_USER_MSG_SIZE) ||
+           (m->msg_iovlen > (unsigned)INT_MAX))
+               return -EMSGSIZE;
+
        if (iocb)
                lock_sock(sk);
 
@@ -648,10 +659,10 @@ static int send_packet(struct kiocb *iocb, struct socket *sock,
                        break;
                }
 
-               res = tipc_send(tport->ref, m->msg_iovlen, m->msg_iov);
-               if (likely(res != -ELINKCONG)) {
+               res = tipc_send(tport->ref, m->msg_iovlen, m->msg_iov,
+                               total_len);
+               if (likely(res != -ELINKCONG))
                        break;
-               }
                if (m->msg_flags & MSG_DONTWAIT) {
                        res = -EWOULDBLOCK;
                        break;
@@ -720,6 +731,12 @@ static int send_stream(struct kiocb *iocb, struct socket *sock,
                goto exit;
        }
 
+       if ((total_len > (unsigned)INT_MAX) ||
+           (m->msg_iovlen > (unsigned)INT_MAX)) {
+               res = -EMSGSIZE;
+               goto exit;
+       }
+
        /*
         * Send each iovec entry using one or more messages
         *
@@ -750,7 +767,8 @@ static int send_stream(struct kiocb *iocb, struct socket *sock,
                                bytes_to_send = curr_left;
                        my_iov.iov_base = curr_start;
                        my_iov.iov_len = bytes_to_send;
-                       if ((res = send_packet(NULL, sock, &my_msg, 0)) < 0) {
+                       res = send_packet(NULL, sock, &my_msg, bytes_to_send);
+                       if (res < 0) {
                                if (bytes_sent)
                                        res = bytes_sent;
                                goto exit;
@@ -845,12 +863,15 @@ static int anc_data_recv(struct msghdr *m, struct tipc_msg *msg,
        if (unlikely(err)) {
                anc_data[0] = err;
                anc_data[1] = msg_data_sz(msg);
-               if ((res = put_cmsg(m, SOL_TIPC, TIPC_ERRINFO, 8, anc_data)))
-                       return res;
-               if (anc_data[1] &&
-                   (res = put_cmsg(m, SOL_TIPC, TIPC_RETDATA, anc_data[1],
-                                   msg_data(msg))))
+               res = put_cmsg(m, SOL_TIPC, TIPC_ERRINFO, 8, anc_data);
+               if (res)
                        return res;
+               if (anc_data[1]) {
+                       res = put_cmsg(m, SOL_TIPC, TIPC_RETDATA, anc_data[1],
+                                      msg_data(msg));
+                       if (res)
+                               return res;
+               }
        }
 
        /* Optionally capture message destination object */
@@ -878,9 +899,11 @@ static int anc_data_recv(struct msghdr *m, struct tipc_msg *msg,
        default:
                has_name = 0;
        }
-       if (has_name &&
-           (res = put_cmsg(m, SOL_TIPC, TIPC_DESTNAME, 12, anc_data)))
-               return res;
+       if (has_name) {
+               res = put_cmsg(m, SOL_TIPC, TIPC_DESTNAME, 12, anc_data);
+               if (res)
+                       return res;
+       }
 
        return 0;
 }
@@ -905,15 +928,13 @@ static int recv_msg(struct kiocb *iocb, struct socket *sock,
        struct tipc_port *tport = tipc_sk_port(sk);
        struct sk_buff *buf;
        struct tipc_msg *msg;
+       long timeout;
        unsigned int sz;
        u32 err;
        int res;
 
        /* Catch invalid receive requests */
 
-       if (m->msg_iovlen != 1)
-               return -EOPNOTSUPP;   /* Don't do multiple iovec entries yet */
-
        if (unlikely(!buf_len))
                return -EINVAL;
 
@@ -924,6 +945,7 @@ static int recv_msg(struct kiocb *iocb, struct socket *sock,
                goto exit;
        }
 
+       timeout = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
 restart:
 
        /* Look for a message in receive queue; wait if necessary */
@@ -933,17 +955,15 @@ restart:
                        res = -ENOTCONN;
                        goto exit;
                }
-               if (flags & MSG_DONTWAIT) {
-                       res = -EWOULDBLOCK;
+               if (timeout <= 0L) {
+                       res = timeout ? timeout : -EWOULDBLOCK;
                        goto exit;
                }
                release_sock(sk);
-               res = wait_event_interruptible(*sk_sleep(sk),
-                       (!skb_queue_empty(&sk->sk_receive_queue) ||
-                        (sock->state == SS_DISCONNECTING)));
+               timeout = wait_event_interruptible_timeout(*sk_sleep(sk),
+                                                          tipc_rx_ready(sock),
+                                                          timeout);
                lock_sock(sk);
-               if (res)
-                       goto exit;
        }
 
        /* Look at first message in receive queue */
@@ -985,11 +1005,10 @@ restart:
                        sz = buf_len;
                        m->msg_flags |= MSG_TRUNC;
                }
-               if (unlikely(copy_to_user(m->msg_iov->iov_base, msg_data(msg),
-                                         sz))) {
-                       res = -EFAULT;
+               res = skb_copy_datagram_iovec(buf, msg_hdr_sz(msg),
+                                             m->msg_iov, sz);
+               if (res)
                        goto exit;
-               }
                res = sz;
        } else {
                if ((sock->state == SS_READY) ||
@@ -1032,19 +1051,15 @@ static int recv_stream(struct kiocb *iocb, struct socket *sock,
        struct tipc_port *tport = tipc_sk_port(sk);
        struct sk_buff *buf;
        struct tipc_msg *msg;
+       long timeout;
        unsigned int sz;
        int sz_to_copy, target, needed;
        int sz_copied = 0;
-       char __user *crs = m->msg_iov->iov_base;
-       unsigned char *buf_crs;
        u32 err;
        int res = 0;
 
        /* Catch invalid receive attempts */
 
-       if (m->msg_iovlen != 1)
-               return -EOPNOTSUPP;   /* Don't do multiple iovec entries yet */
-
        if (unlikely(!buf_len))
                return -EINVAL;
 
@@ -1057,7 +1072,7 @@ static int recv_stream(struct kiocb *iocb, struct socket *sock,
        }
 
        target = sock_rcvlowat(sk, flags & MSG_WAITALL, buf_len);
-
+       timeout = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
 restart:
 
        /* Look for a message in receive queue; wait if necessary */
@@ -1067,17 +1082,15 @@ restart:
                        res = -ENOTCONN;
                        goto exit;
                }
-               if (flags & MSG_DONTWAIT) {
-                       res = -EWOULDBLOCK;
+               if (timeout <= 0L) {
+                       res = timeout ? timeout : -EWOULDBLOCK;
                        goto exit;
                }
                release_sock(sk);
-               res = wait_event_interruptible(*sk_sleep(sk),
-                       (!skb_queue_empty(&sk->sk_receive_queue) ||
-                        (sock->state == SS_DISCONNECTING)));
+               timeout = wait_event_interruptible_timeout(*sk_sleep(sk),
+                                                          tipc_rx_ready(sock),
+                                                          timeout);
                lock_sock(sk);
-               if (res)
-                       goto exit;
        }
 
        /* Look at first message in receive queue */
@@ -1106,24 +1119,25 @@ restart:
        /* Capture message data (if valid) & compute return value (always) */
 
        if (!err) {
-               buf_crs = (unsigned char *)(TIPC_SKB_CB(buf)->handle);
-               sz = (unsigned char *)msg + msg_size(msg) - buf_crs;
+               u32 offset = (u32)(unsigned long)(TIPC_SKB_CB(buf)->handle);
 
+               sz -= offset;
                needed = (buf_len - sz_copied);
                sz_to_copy = (sz <= needed) ? sz : needed;
-               if (unlikely(copy_to_user(crs, buf_crs, sz_to_copy))) {
-                       res = -EFAULT;
+
+               res = skb_copy_datagram_iovec(buf, msg_hdr_sz(msg) + offset,
+                                             m->msg_iov, sz_to_copy);
+               if (res)
                        goto exit;
-               }
+
                sz_copied += sz_to_copy;
 
                if (sz_to_copy < sz) {
                        if (!(flags & MSG_PEEK))
-                               TIPC_SKB_CB(buf)->handle = buf_crs + sz_to_copy;
+                               TIPC_SKB_CB(buf)->handle =
+                               (void *)(unsigned long)(offset + sz_to_copy);
                        goto exit;
                }
-
-               crs += sz_to_copy;
        } else {
                if (sz_copied != 0)
                        goto exit; /* can't add error msg to valid data */
@@ -1250,7 +1264,7 @@ static u32 filter_rcv(struct sock *sk, struct sk_buff *buf)
 
        /* Enqueue message (finally!) */
 
-       TIPC_SKB_CB(buf)->handle = msg_data(msg);
+       TIPC_SKB_CB(buf)->handle = 0;
        atomic_inc(&tipc_queue_size);
        __skb_queue_tail(&sk->sk_receive_queue, buf);
 
@@ -1410,9 +1424,8 @@ static int connect(struct socket *sock, struct sockaddr *dest, int destlen,
        m.msg_name = dest;
        m.msg_namelen = destlen;
        res = send_msg(NULL, sock, &m, 0);
-       if (res < 0) {
+       if (res < 0)
                goto exit;
-       }
 
        /* Wait until an 'ACK' or 'RST' arrives, or a timeout occurs */
 
@@ -1434,11 +1447,10 @@ static int connect(struct socket *sock, struct sockaddr *dest, int destlen,
                                        advance_rx_queue(sk);
                        }
                } else {
-                       if (sock->state == SS_CONNECTED) {
+                       if (sock->state == SS_CONNECTED)
                                res = -EISCONN;
-                       } else {
+                       else
                                res = -ECONNREFUSED;
-                       }
                }
        } else {
                if (res == 0)
@@ -1604,7 +1616,7 @@ restart:
                buf = __skb_dequeue(&sk->sk_receive_queue);
                if (buf) {
                        atomic_dec(&tipc_queue_size);
-                       if (TIPC_SKB_CB(buf)->handle != msg_data(buf_msg(buf))) {
+                       if (TIPC_SKB_CB(buf)->handle != 0) {
                                buf_discard(buf);
                                goto restart;
                        }
@@ -1664,7 +1676,8 @@ static int setsockopt(struct socket *sock,
                return -ENOPROTOOPT;
        if (ol < sizeof(value))
                return -EINVAL;
-       if ((res = get_user(value, (u32 __user *)ov)))
+       res = get_user(value, (u32 __user *)ov);
+       if (res)
                return res;
 
        lock_sock(sk);
@@ -1722,7 +1735,8 @@ static int getsockopt(struct socket *sock,
                return put_user(0, ol);
        if (lvl != SOL_TIPC)
                return -ENOPROTOOPT;
-       if ((res = get_user(len, ol)))
+       res = get_user(len, ol);
+       if (res)
                return res;
 
        lock_sock(sk);