+#include <linux/net.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+
+#include "p2pkp.h"
+#include "p2pkp_net.h"
+#include "p2pkp_sock.h"
+#include "p2pkp_debug.h"
+#include "p2pkp_file.h"
+
+
+static int p2pkp_release(struct socket *sock);
+static int p2pkp_bind(struct socket *sock, struct sockaddr *myaddr,
+ int sockaddr_len);
+static int p2pkp_connect(struct socket *sock, struct sockaddr *vaddr,
+ int sockaddr_len, int flags);
+static int p2pkp_sendmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *m, size_t total_len);
+static int p2pkp_recvmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *m, size_t total_len, int flags);
+
+/* creates a socket */
+static int p2pkp_create(struct net *net, struct socket *sock, int protocol,
+ int kern);
+
+/* protocol */
+static struct proto p2pkp_proto = {
+ .name = P2PKP_PROTO_NAME,
+ .owner = THIS_MODULE,
+ .obj_size = sizeof(struct p2pkp_sock),
+};
+
+/* protocol operations */
+static const struct proto_ops p2pkp_ops = {
+ .family = PF_P2PKP,
+ .owner = THIS_MODULE,
+ .release = p2pkp_release,
+ .bind = p2pkp_bind,
+ .connect = p2pkp_connect,
+ .sendmsg = p2pkp_sendmsg,
+ .recvmsg = p2pkp_recvmsg,
+
+ /* unimplemented */
+ .poll = datagram_poll,
+ .socketpair = sock_no_socketpair,
+ .accept = sock_no_accept,
+ .getname = sock_no_getname,
+ .ioctl = sock_no_ioctl,
+ .listen = sock_no_listen,
+ .shutdown = sock_no_shutdown,
+ .setsockopt = sock_no_setsockopt,
+ .getsockopt = sock_no_getsockopt,
+ .mmap = sock_no_mmap,
+ .sendpage = sock_no_sendpage,
+};
+
+
+/* family socket */
+static const struct net_proto_family p2pkp_family_ops = {
+ .family = PF_P2PKP,
+ .create = p2pkp_create,
+ .owner = THIS_MODULE,
+};
+
+
+
+int p2pkp_register_protocol(void)
+{
+ int err;
+
+ err = sock_register(&p2pkp_family_ops);
+ if (err) {
+ ERROR("cannot register operations");
+ goto exit;
+ }
+
+ err = proto_register(&p2pkp_proto, 0);
+ if (err) {
+ ERROR("cannot register proto");
+ goto sock_unreg;
+ }
+ DEBUG("Successfully registered socket operations");
+
+ return 0;
+
+sock_unreg:
+ sock_unregister(PF_P2PKP);
+exit:
+ return err;
+
+}
+
+void p2pkp_unregister_protocol(void)
+{
+ DEBUG("Unregistering protocol");
+ proto_unregister(&p2pkp_proto);
+ sock_unregister(PF_P2PKP);
+ DEBUG("Successfully UNregistered socket operations");
+}
+
+
+static int p2pkp_release(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+ struct p2pkp_sock *ps = sock2p2pkp(sk);
+
+ if (!sk)
+ return 0;
+
+ lock_sock(sk);
+ sock_orphan(sk);
+ sk->sk_shutdown = SEND_SHUTDOWN;
+
+ skb_queue_purge(&sk->sk_receive_queue);
+ skb_queue_purge(&sk->sk_write_queue);
+ skb_queue_purge(&sk->sk_error_queue);
+
+ /* TODO: some statistics, don't know if really
+ * needed */
+ //sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
+
+ /* TODO do whatever is needed with ps */
+
+ if (!sock_flag(sk, SOCK_DEAD))
+ sk->sk_state_change(sk);
+
+ release_sock(sk);
+
+ sock_set_flag(sk, SOCK_DEAD);
+ sock->sk = NULL;
+
+ p2pkp_destroy_udp_sock(ps->net_sock);
+
+ sock_put(sk);
+ DEBUG("Released the socket");
+ return 0;
+}
+
+static int p2pkp_bind(struct socket *sock, struct sockaddr *myaddr,
+ int sockaddr_len){return 0;}
+
+
+static int p2pkp_connect(struct socket *sock, struct sockaddr *vaddr,
+ int sockaddr_len, int flags)
+{
+ struct sock *sk = sock->sk;
+ struct p2pkp_sock *ps = sock2p2pkp(sk);
+ struct p2pkp_conn_info *pconn;
+
+ if (!sk || !vaddr || sockaddr_len != sizeof(struct sockaddr_in) ||
+ vaddr->sa_family != AF_INET) {
+ ERROR("invalid socket parameters");
+ return -EINVAL;
+ }
+
+ pconn = kmalloc(sizeof(struct p2pkp_conn_info), GFP_KERNEL);
+ if (!pconn) {
+ ERROR("no more memory for new list element");
+ return -ENOMEM;
+ }
+
+ INIT_LIST_HEAD(&pconn->list);
+ memcpy(&pconn->sin, vaddr, sockaddr_len);
+
+ lock_sock(sk);
+
+ /* TODO spinlock required? */
+ list_add(&pconn->list, &ps->conn_list);
+
+ sock->state = SS_CONNECTED;
+ release_sock(sk);
+
+ DEBUG("Connected socket to a peer");
+
+ return 0;
+}
+
+static int p2pkp_sendmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *m, size_t total_len)
+{
+ int err = -ENXIO;
+ int f_index, bytes_read;
+ struct sock *sk = sock->sk;
+ struct p2pkp_sock *ps = sock2p2pkp(sk);
+ struct sockaddr_in *sin = (struct sockaddr_in *)m->msg_name;
+ struct file * file;
+ char *file_name;
+ struct list_head *pos;
+ struct p2pkp_conn_info *conn;
+
+ if (!sk || !sock ||
+ (sin && sizeof(struct sockaddr_in) != m->msg_namelen)) {
+ ERROR("invalid socket parameters");
+ return -EINVAL;
+ }
+
+ if (sin && ((err = p2pkp_connect(sock, (struct sockaddr *)sin,
+ sizeof(struct sockaddr_in), 0)) < 0)) {
+ ERROR("cannot connect to socket");
+ return err;
+ }
+
+ lock_sock(sk);
+ if (!(sock->state & SS_CONNECTED)) {
+ ERROR("unconnected socket");
+ err = -ECONNREFUSED;
+ goto release;
+ }
+ release_sock(sk);
+
+ /* check if the file exists */
+ for (f_index = 0; f_index < m->msg_iovlen; f_index++) {
+ file_name = (char *)m->msg_iov[f_index].iov_base;
+ file = p2pkp_open_file_read(file_name);
+ if (!file) {
+ ERROR("cannot open file %s\n", file_name);
+ continue;
+ }
+ DEBUG("opened file %s", file_name);
+ for (;;) {
+ bytes_read = p2pkp_read_from_file(file, ps->buffer, ps->buffer_len);
+ DEBUG("read %d bytes from file", bytes_read);
+ if (bytes_read < 0) {
+ ERROR("cannot read from file");
+ goto out_close;
+ }
+ if (bytes_read == 0)
+ goto out_close;
+
+ lock_sock(sk);
+ /* iterate through all the connections to send the data */
+ list_for_each(pos, &ps->conn_list) {
+
+ conn = list_entry(pos, struct p2pkp_conn_info, list);
+ err = p2pkp_msgsend(ps->net_sock,
+ &conn->sin, ps->buffer, ps->buffer_len);
+ if (err < 0) {
+ ERROR("can't send first message");
+ goto out_conn;
+ }
+ }
+out_conn:
+ release_sock(sk);
+ }
+
+out_close:
+
+ p2pkp_close_file(file);
+ }
+
+
+ DEBUG("Sending completed successfully");
+ err = 0;
+release:
+ release_sock(sk);
+ return err;
+}
+
+
+static int p2pkp_recvmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *m, size_t total_len, int flags){return 0;}
+
+
+static int p2pkp_create(struct net *net, struct socket *sock, int protocol,
+ int kern)
+{
+ struct sock *sk = NULL;
+ struct p2pkp_sock *ps;
+ int err = -ESOCKTNOSUPPORT;
+
+ DEBUG("Creating new P2PKP socket");
+ if (sock->type != SOCK_DGRAM) {
+ ERROR("P2PKP supports only SOCK_DGRAM sockets");
+ goto error;
+ }
+ if (protocol) {
+ ERROR("protocol %x not suported - currently only 0 is supported",
+ protocol);
+ goto error;
+ }
+
+ sock->state = SS_UNCONNECTED;
+
+ err = -ENOBUFS;
+ sk = sk_alloc(net, PF_P2PKP, GFP_KERNEL, &p2pkp_proto);
+ if (unlikely(!sk)) {
+ ERROR("unable to create new socket");
+ goto error;
+ }
+
+ sock->ops = &p2pkp_ops;
+ sock_init_data(sock, sk);
+
+ sk->sk_reuse = 1;
+ sk->sk_no_check = 1;
+ sk->sk_family = PF_P2PKP;
+ sk->sk_protocol = protocol;
+ sk->sk_backlog_rcv = sk->sk_prot->backlog_rcv;
+
+ /* TODO init further data */
+ ps = sock2p2pkp(sk);
+
+ err = p2pkp_create_udp_sock(&ps->net_sock);
+ if (err < 0) {
+ ERROR("cannot create UDP socket");
+ goto error;
+ }
+ ps->buffer_len = P2PKP_DEFAULT_BUF_LEN;
+
+ INIT_LIST_HEAD(&ps->conn_list);
+ spin_lock_init(&ps->conn_lock);
+
+ DEBUG("Created new P2PKP socket");
+
+ return 0;
+error:
+ if (sk)
+ sock_put(sk);
+ return err;
+}
+
+