From 55f4e4a5ec657a017e3bf75299ad71fd1c968dd3 Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Tue, 21 Oct 2008 07:00:00 -0700 Subject: Initial Contribution --- slirp/socket.c | 245 ++++++++++++++++++++++++++++++++------------------------- 1 file changed, 140 insertions(+), 105 deletions(-) (limited to 'slirp/socket.c') diff --git a/slirp/socket.c b/slirp/socket.c index 0ae1f87..8ddc95c 100644 --- a/slirp/socket.c +++ b/slirp/socket.c @@ -1,7 +1,7 @@ /* * Copyright (c) 1995 Danny Gasparovski. - * - * Please read the file COPYRIGHT for the + * + * Please read the file COPYRIGHT for the * terms and conditions of the copyright. */ @@ -12,6 +12,9 @@ #ifdef __sun__ #include #endif +#define SLIRP_COMPILATION +#include "sockets.h" +#include "proxy_common.h" void so_init() @@ -29,19 +32,19 @@ solookup(head, laddr, lport, faddr, fport) u_int fport; { struct socket *so; - + for (so = head->so_next; so != head; so = so->so_next) { - if (so->so_lport == lport && + if (so->so_lport == lport && so->so_laddr.s_addr == laddr.s_addr && so->so_faddr.s_addr == faddr.s_addr && so->so_fport == fport) break; } - + if (so == head) return (struct socket *)NULL; return so; - + } /* @@ -53,7 +56,7 @@ struct socket * socreate() { struct socket *so; - + so = (struct socket *)malloc(sizeof(struct socket)); if(so) { memset(so, 0, sizeof(struct socket)); @@ -70,7 +73,10 @@ void sofree(so) struct socket *so; { - if (so->so_emu==EMU_RSH && so->extra) { + if (so->so_state & SS_PROXIFIED) + proxy_manager_del(so->s); + + if (so->extra) { sofree(so->extra); so->extra=NULL; } @@ -78,10 +84,10 @@ sofree(so) tcp_last_so = &tcb; else if (so == udp_last_so) udp_last_so = &udb; - - m_free(so->so_m); - - if(so->so_next && so->so_prev) + + mbuf_free(so->so_m); + + if(so->so_next && so->so_prev) remque(so); /* crashes if so is not in a queue */ free(so); @@ -97,21 +103,21 @@ soread(so) struct socket *so; { int n, nn, lss, total; - struct sbuf *sb = &so->so_snd; - int len = sb->sb_datalen - sb->sb_cc; + SBuf sb = &so->so_snd; + int len = sb->sb_datalen - sb->sb_cc; struct iovec iov[2]; int mss = so->so_tcpcb->t_maxseg; - + DEBUG_CALL("soread"); DEBUG_ARG("so = %lx", (long )so); - - /* + + /* * No need to check if there's enough room to read. * soread wouldn't have been called if there weren't */ - + len = sb->sb_datalen - sb->sb_cc; - + iov[0].iov_base = sb->sb_wptr; if (sb->sb_wptr < sb->sb_rptr) { iov[0].iov_len = sb->sb_rptr - sb->sb_wptr; @@ -150,13 +156,13 @@ soread(so) n = 1; } } - + #ifdef HAVE_READV nn = readv(so->s, (struct iovec *)iov, n); DEBUG_MISC((dfd, " ... read nn = %d bytes\n", nn)); #else nn = recv(so->s, iov[0].iov_base, iov[0].iov_len,0); -#endif +#endif if (nn <= 0) { if (nn < 0 && (errno == EINTR || errno == EAGAIN)) return 0; @@ -167,7 +173,7 @@ soread(so) return -1; } } - + #ifndef HAVE_READV /* * If there was no error, try and read the second time round @@ -184,10 +190,10 @@ soread(so) if (ret > 0) nn += ret; } - + DEBUG_MISC((dfd, " ... read nn = %d bytes\n", nn)); #endif - + /* Update fields */ sb->sb_cc += nn; sb->sb_wptr += nn; @@ -195,10 +201,10 @@ soread(so) sb->sb_wptr -= sb->sb_datalen; return nn; } - + /* * Get urgent data - * + * * When the socket is created, we set it SO_OOBINLINE, * so when OOB data arrives, we soread() it and everything * in the send buffer is sent as urgent data @@ -211,13 +217,13 @@ sorecvoob(so) DEBUG_CALL("sorecvoob"); DEBUG_ARG("so = %lx", (long)so); - + /* * We take a guess at how much urgent data has arrived. * In most situations, when urgent data arrives, the next * read() should get all the urgent data. This guess will * be wrong however if more data arrives just after the - * urgent data, or the read() doesn't return all the + * urgent data, or the read() doesn't return all the * urgent data. */ soread(so); @@ -235,26 +241,25 @@ int sosendoob(so) struct socket *so; { - struct sbuf *sb = &so->so_rcv; - char buff[2048]; /* XXX Shouldn't be sending more oob data than this */ - - int n, len; - + SBuf sb = &so->so_rcv; + char buff[2048]; /* XXX Shouldn't be sending more oob data than this */ + int n, len; + DEBUG_CALL("sosendoob"); DEBUG_ARG("so = %lx", (long)so); DEBUG_ARG("sb->sb_cc = %d", sb->sb_cc); - + if (so->so_urgc > 2048) so->so_urgc = 2048; /* XXXX */ - + if (sb->sb_rptr < sb->sb_wptr) { /* We can send it directly */ n = send(so->s, sb->sb_rptr, so->so_urgc, (MSG_OOB)); /* |MSG_DONTWAIT)); */ so->so_urgc -= n; - + DEBUG_MISC((dfd, " --- sent %d bytes urgent data, %d urgent bytes left\n", n, so->so_urgc)); } else { - /* + /* * Since there's no sendv or sendtov like writev, * we must copy all data to a linear buffer then * send it all @@ -274,34 +279,34 @@ sosendoob(so) #ifdef DEBUG if (n != len) DEBUG_ERROR((dfd, "Didn't send all data urgently XXXXX\n")); -#endif +#endif DEBUG_MISC((dfd, " ---2 sent %d bytes urgent data, %d urgent bytes left\n", n, so->so_urgc)); } - + sb->sb_cc -= n; sb->sb_rptr += n; if (sb->sb_rptr >= (sb->sb_data + sb->sb_datalen)) sb->sb_rptr -= sb->sb_datalen; - + return n; } /* - * Write data from so_rcv to so's socket, + * Write data from so_rcv to so's socket, * updating all sbuf field as necessary */ int sowrite(so) struct socket *so; { - int n,nn; - struct sbuf *sb = &so->so_rcv; - int len = sb->sb_cc; + int n,nn; + SBuf sb = &so->so_rcv; + int len = sb->sb_cc; struct iovec iov[2]; - + DEBUG_CALL("sowrite"); DEBUG_ARG("so = %lx", (long)so); - + if (so->so_urgc) { sosendoob(so); if (sb->sb_cc == 0) @@ -312,9 +317,9 @@ sowrite(so) * No need to check if there's something to write, * sowrite wouldn't have been called otherwise */ - + len = sb->sb_cc; - + iov[0].iov_base = sb->sb_rptr; if (sb->sb_rptr < sb->sb_wptr) { iov[0].iov_len = sb->sb_wptr - sb->sb_rptr; @@ -337,7 +342,7 @@ sowrite(so) #ifdef HAVE_READV nn = writev(so->s, (const struct iovec *)iov, n); - + DEBUG_MISC((dfd, " ... wrote nn = %d bytes\n", nn)); #else nn = send(so->s, iov[0].iov_base, iov[0].iov_len,0); @@ -345,7 +350,7 @@ sowrite(so) /* This should never happen, but people tell me it does *shrug* */ if (nn < 0 && (errno == EAGAIN || errno == EINTR)) return 0; - + if (nn <= 0) { DEBUG_MISC((dfd, " --- sowrite disconnected, so->so_state = %x, errno = %d\n", so->so_state, errno)); @@ -353,7 +358,7 @@ sowrite(so) tcp_sockclosed(sototcpcb(so)); return -1; } - + #ifndef HAVE_READV if (n == 2 && nn == iov[0].iov_len) { int ret; @@ -363,20 +368,20 @@ sowrite(so) } DEBUG_MISC((dfd, " ... wrote nn = %d bytes\n", nn)); #endif - + /* Update sbuf */ sb->sb_cc -= nn; sb->sb_rptr += nn; if (sb->sb_rptr >= (sb->sb_data + sb->sb_datalen)) sb->sb_rptr -= sb->sb_datalen; - + /* * If in DRAIN mode, and there's no more data, set * it CANTSENDMORE */ if ((so->so_state & SS_FWDRAIN) && sb->sb_cc == 0) sofcantsendmore(so); - + return nn; } @@ -389,68 +394,68 @@ sorecvfrom(so) { struct sockaddr_in addr; int addrlen = sizeof(struct sockaddr_in); - + DEBUG_CALL("sorecvfrom"); DEBUG_ARG("so = %lx", (long)so); - + if (so->so_type == IPPROTO_ICMP) { /* This is a "ping" reply */ char buff[256]; int len; - - len = recvfrom(so->s, buff, 256, 0, + + len = recvfrom(so->s, buff, 256, 0, (struct sockaddr *)&addr, &addrlen); /* XXX Check if reply is "correct"? */ - + if(len == -1 || len == 0) { u_char code=ICMP_UNREACH_PORT; if(errno == EHOSTUNREACH) code=ICMP_UNREACH_HOST; else if(errno == ENETUNREACH) code=ICMP_UNREACH_NET; - + DEBUG_MISC((dfd," udp icmp rx errno = %d-%s\n", errno,strerror(errno))); icmp_error(so->so_m, ICMP_UNREACH,code, 0,strerror(errno)); } else { icmp_reflect(so->so_m); - so->so_m = 0; /* Don't m_free() it again! */ + so->so_m = 0; /* Don't mbuf_free() it again! */ } /* No need for this socket anymore, udp_detach it */ udp_detach(so); } else { /* A "normal" UDP packet */ - struct mbuf *m; + MBuf m; int len, n; - if (!(m = m_get())) return; + if (!(m = mbuf_alloc())) return; m->m_data += if_maxlinkhdr; - - /* + + /* * XXX Shouldn't FIONREAD packets destined for port 53, * but I don't know the max packet size for DNS lookups */ - len = M_FREEROOM(m); + len = mbuf_freeroom(m); /* if (so->so_fport != htons(53)) { */ ioctlsocket(so->s, FIONREAD, &n); - + if (n > len) { n = (m->m_data - m->m_dat) + m->m_len + n + 1; - m_inc(m, n); - len = M_FREEROOM(m); + mbuf_ensure(m, n); + len = mbuf_freeroom(m); } /* } */ - + m->m_len = recvfrom(so->s, m->m_data, len, 0, (struct sockaddr *)&addr, &addrlen); - DEBUG_MISC((dfd, " did recvfrom %d, errno = %d-%s\n", + DEBUG_MISC((dfd, " did recvfrom %d, errno = %d-%s\n", m->m_len, errno,strerror(errno))); if(m->m_len<0) { u_char code=ICMP_UNREACH_PORT; if(errno == EHOSTUNREACH) code=ICMP_UNREACH_HOST; else if(errno == ENETUNREACH) code=ICMP_UNREACH_NET; - + DEBUG_MISC((dfd," rx error, tx icmp ICMP_UNREACH:%i\n", code)); icmp_error(so->so_m, ICMP_UNREACH,code, 0,strerror(errno)); - m_free(m); + mbuf_free(m); } else { /* * Hack: domain name lookup will be used the most for UDP, @@ -466,12 +471,12 @@ sorecvfrom(so) } /* if (m->m_len == len) { - * m_inc(m, MINCSIZE); + * mbuf_ensure(m, MINCSIZE); * m->m_len = 0; * } */ - - /* + + /* * If this packet was destined for CTL_ADDR, * make it look like that's where it came from, done by udp_output */ @@ -486,7 +491,7 @@ sorecvfrom(so) int sosendto(so, m) struct socket *so; - struct mbuf *m; + MBuf m; { int ret; struct sockaddr_in addr; @@ -494,31 +499,28 @@ sosendto(so, m) DEBUG_CALL("sosendto"); DEBUG_ARG("so = %lx", (long)so); DEBUG_ARG("m = %lx", (long)m); - + addr.sin_family = AF_INET; if ((so->so_faddr.s_addr & htonl(0xffffff00)) == special_addr.s_addr) { /* It's an alias */ - switch(ntohl(so->so_faddr.s_addr) & 0xff) { - case CTL_DNS: - addr.sin_addr = dns_addr; - break; - case CTL_ALIAS: - default: - addr.sin_addr = loopback_addr; - break; - } + int low = ntohl(so->so_faddr.s_addr) & 0xff; + + if ( CTL_IS_DNS(low) ) + addr.sin_addr = dns_addr[low - CTL_DNS]; + else + addr.sin_addr = loopback_addr; } else addr.sin_addr = so->so_faddr; addr.sin_port = so->so_fport; DEBUG_MISC((dfd, " sendto()ing, addr.sin_port=%d, addr.sin_addr.s_addr=%.16s\n", ntohs(addr.sin_port), inet_ntoa(addr.sin_addr))); - + /* Don't care what port we get */ ret = sendto(so->s, m->m_data, m->m_len, 0, (struct sockaddr *)&addr, sizeof (struct sockaddr)); if (ret < 0) return -1; - + /* * Kill the socket if there's no reply in 4 minutes, * but only if it's an expirable socket @@ -548,39 +550,47 @@ solisten(port, laddr, lport, flags) DEBUG_ARG("laddr = %x", laddr); DEBUG_ARG("lport = %d", lport); DEBUG_ARG("flags = %x", flags); - + if ((so = socreate()) == NULL) { /* free(so); Not sofree() ??? free(NULL) == NOP */ return NULL; } - + /* Don't tcp_attach... we don't need so_snd nor so_rcv */ if ((so->so_tcpcb = tcp_newtcpcb(so)) == NULL) { free(so); return NULL; } insque(so,&tcb); - - /* + + /* * SS_FACCEPTONCE sockets must time out. */ if (flags & SS_FACCEPTONCE) so->so_tcpcb->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT*2; - + so->so_state = (SS_FACCEPTCONN|flags); so->so_lport = lport; /* Kept in network format */ + so->so_hport = port; so->so_laddr.s_addr = laddr; /* Ditto */ - + + memset( &addr, 0, sizeof(addr) ); addr.sin_family = AF_INET; - addr.sin_addr.s_addr = INADDR_ANY; + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); addr.sin_port = port; - - if (((s = socket(AF_INET,SOCK_STREAM,0)) < 0) || - (setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,sizeof(int)) < 0) || - (bind(s,(struct sockaddr *)&addr, sizeof(addr)) < 0) || + + if ((s = socket(AF_INET,SOCK_STREAM,0)) < 0) { + sofree(so); + return NULL; + } + + socket_set_xreuseaddr(s); + + if ((socket_set_xreuseaddr(s) < 0) || + (bind(s,(struct sockaddr *)&addr, sizeof(addr)) < 0) || (listen(s,1) < 0)) { int tmperrno = errno; /* Don't clobber the real reason we failed */ - + close(s); sofree(so); /* Restore the real errno */ @@ -591,8 +601,8 @@ solisten(port, laddr, lport, flags) #endif return NULL; } - setsockopt(s,SOL_SOCKET,SO_OOBINLINE,(char *)&opt,sizeof(int)); - + socket_set_oobinline(s); + getsockname(s,(struct sockaddr *)&addr,&addrlen); so->so_fport = addr.sin_port; if (addr.sin_addr.s_addr == 0 || addr.sin_addr.s_addr == loopback_addr.s_addr) @@ -604,7 +614,32 @@ solisten(port, laddr, lport, flags) return so; } -/* + +int +sounlisten(u_int port) +{ + struct socket *so; + + for (so = tcb.so_next; so != &tcb; so = so->so_next) { + if (so->so_hport == htons(port)) { + break; + } + } + + if (so == &tcb) { + return -1; + } + + sofcantrcvmore( so ); + sofcantsendmore( so ); + close( so->s ); + so->s = -1; + sofree( so ); + return 0; +} + + +/* * Data is available in so_rcv * Just write() the data to the socket * XXX not yet... @@ -616,7 +651,7 @@ sorwakeup(so) /* sowrite(so); */ /* FD_CLR(so->s,&writefds); */ } - + /* * Data has been freed in so_snd * We have room for a read() if we want to -- cgit v1.1