diff --git a/sys/kern/uipc_sockbuf.c b/sys/kern/uipc_sockbuf.c index 6184c233782..baeb5c7e3df 100644 --- a/sys/kern/uipc_sockbuf.c +++ b/sys/kern/uipc_sockbuf.c @@ -53,6 +53,15 @@ #include /* for aio_swake proto */ #include +/* callbacks/functions for delay accept routines */ +/* socket needs _any_ data to be accepted */ +static void sohasdata(struct socket *so, void *arg, int waitflag); +/* check for GET/POST */ +static void sohashttpgetorpost(struct socket *so, void *arg, int waitflag); +/* check for end of HTTP request */ +static void soishttpconnected(struct socket *so, void *arg, int waitflag); +static char sbindex(struct mbuf **mp, int *begin, int end); + int maxsockets; /* @@ -102,15 +111,151 @@ soisconnecting(so) so->so_state |= SS_ISCONNECTING; } +static char +sbindex(struct mbuf **mp, int *begin, int end) +{ + struct mbuf *m = *mp; + int diff = end - *begin + 1; + + while (m->m_len < diff) { + *begin += m->m_len; + diff -= m->m_len; + if (m->m_next) { + m = m->m_next; + } else if (m->m_nextpkt) { + m = m->m_nextpkt; + } else { + panic("sbindex: not enough data"); + } + } + *mp = m; + return *(mtod(m, char *) + diff - 1); +} + +static void +sohashttpgetorpost(struct socket *so, void *arg, int waitflag) +{ + + if ((so->so_state & SS_CANTRCVMORE) == 0) { + struct mbuf *m; + + if (so->so_rcv.sb_cc < 6) + return; + m = so->so_rcv.sb_mb; + if (bcmp(mtod(m, char *), "GET ", 4) == 0 || + bcmp(mtod(m, char *), "POST ", 5) == 0) { + soishttpconnected(so, arg, waitflag); + } + } + + so->so_upcall = NULL; + so->so_rcv.sb_flags &= ~SB_UPCALL; + soisconnected(so); + return; +} + +static void +soishttpconnected(struct socket *so, void *arg, int waitflag) +{ + char a, b, c; + struct mbuf *y, *z; + + if ((so->so_state & SS_CANTRCVMORE) == 0) { + /* seek to end and keep track of next to last mbuf */ + y = so->so_rcv.sb_mb; + while (y->m_nextpkt) + y = y->m_nextpkt; + z = y; + while (y->m_next) { + z = y; + y = y->m_next; + } + + if (z->m_len + y->m_len > 2) { + int index = y->m_len - 1; + + c = *(mtod(y, char *) + index--); + switch (index) { + case -1: + y = z; + index = y->m_len - 1; + b = *(mtod(y, char *) + index--); + break; + case 0: + b = *(mtod(y, char *) + index--); + y = z; + index = y->m_len - 1; + break; + default: + b = *(mtod(y, char *) + index--); + break; + } + a = *(mtod(y, char *) + index--); + } else { + int begin = 0; + int end = so->so_rcv.sb_cc - 3; + + y = so->so_rcv.sb_mb; + a = sbindex(&y, &begin, end++); + b = sbindex(&y, &begin, end++); + c = sbindex(&y, &begin, end++); + } + + if (c == '\n' && (b == '\n' || (b == '\r' && a == '\n'))) { + /* we have all request headers */ + goto done; + } else { + /* still need more data */ + so->so_upcall = soishttpconnected; + so->so_rcv.sb_flags |= SB_UPCALL; + return; + } + } + +done: + so->so_upcall = NULL; + so->so_rcv.sb_flags &= ~SB_UPCALL; + soisconnected(so); + return; +} + +static void +sohasdata(struct socket *so, void *arg, int waitflag) +{ + + if (!soreadable(so)) { + return; + } + + so->so_upcall = NULL; + so->so_rcv.sb_flags &= ~SB_UPCALL; + soisconnected(so); + return; +} + void soisconnected(so) - register struct socket *so; + struct socket *so; { - register struct socket *head = so->so_head; + struct socket *head = so->so_head; + int s; so->so_state &= ~(SS_ISCONNECTING|SS_ISDISCONNECTING|SS_ISCONFIRMING); so->so_state |= SS_ISCONNECTED; if (head && (so->so_state & SS_INCOMP)) { + if ((so->so_options & (SO_DELAYACCEPT|SO_HTTPACCEPT)) != 0) { + s = splnet(); + if (!soreadable(so)) { + if ((so->so_options & SO_DELAYACCEPT) != 0) + so->so_upcall = sohasdata; + else + so->so_upcall = sohashttpgetorpost; + so->so_rcv.sb_flags |= SB_UPCALL; + splx(s); + return; + } + splx(s); + } TAILQ_REMOVE(&head->so_incomp, so, so_list); head->so_incqlen--; so->so_state &= ~SS_INCOMP; diff --git a/sys/kern/uipc_socket.c b/sys/kern/uipc_socket.c index b172d46b4df..e83b7827c75 100644 --- a/sys/kern/uipc_socket.c +++ b/sys/kern/uipc_socket.c @@ -1050,6 +1050,8 @@ sosetopt(so, sopt) case SO_REUSEPORT: case SO_OOBINLINE: case SO_TIMESTAMP: + case SO_DELAYACCEPT: + case SO_HTTPACCEPT: error = sooptcopyin(sopt, &optval, sizeof optval, sizeof optval); if (error) @@ -1215,6 +1217,8 @@ sogetopt(so, sopt) case SO_BROADCAST: case SO_OOBINLINE: case SO_TIMESTAMP: + case SO_DELAYACCEPT: + case SO_HTTPACCEPT: optval = so->so_options & sopt->sopt_name; integer: error = sooptcopyout(sopt, &optval, sizeof optval); diff --git a/sys/kern/uipc_socket2.c b/sys/kern/uipc_socket2.c index 6184c233782..baeb5c7e3df 100644 --- a/sys/kern/uipc_socket2.c +++ b/sys/kern/uipc_socket2.c @@ -53,6 +53,15 @@ #include /* for aio_swake proto */ #include +/* callbacks/functions for delay accept routines */ +/* socket needs _any_ data to be accepted */ +static void sohasdata(struct socket *so, void *arg, int waitflag); +/* check for GET/POST */ +static void sohashttpgetorpost(struct socket *so, void *arg, int waitflag); +/* check for end of HTTP request */ +static void soishttpconnected(struct socket *so, void *arg, int waitflag); +static char sbindex(struct mbuf **mp, int *begin, int end); + int maxsockets; /* @@ -102,15 +111,151 @@ soisconnecting(so) so->so_state |= SS_ISCONNECTING; } +static char +sbindex(struct mbuf **mp, int *begin, int end) +{ + struct mbuf *m = *mp; + int diff = end - *begin + 1; + + while (m->m_len < diff) { + *begin += m->m_len; + diff -= m->m_len; + if (m->m_next) { + m = m->m_next; + } else if (m->m_nextpkt) { + m = m->m_nextpkt; + } else { + panic("sbindex: not enough data"); + } + } + *mp = m; + return *(mtod(m, char *) + diff - 1); +} + +static void +sohashttpgetorpost(struct socket *so, void *arg, int waitflag) +{ + + if ((so->so_state & SS_CANTRCVMORE) == 0) { + struct mbuf *m; + + if (so->so_rcv.sb_cc < 6) + return; + m = so->so_rcv.sb_mb; + if (bcmp(mtod(m, char *), "GET ", 4) == 0 || + bcmp(mtod(m, char *), "POST ", 5) == 0) { + soishttpconnected(so, arg, waitflag); + } + } + + so->so_upcall = NULL; + so->so_rcv.sb_flags &= ~SB_UPCALL; + soisconnected(so); + return; +} + +static void +soishttpconnected(struct socket *so, void *arg, int waitflag) +{ + char a, b, c; + struct mbuf *y, *z; + + if ((so->so_state & SS_CANTRCVMORE) == 0) { + /* seek to end and keep track of next to last mbuf */ + y = so->so_rcv.sb_mb; + while (y->m_nextpkt) + y = y->m_nextpkt; + z = y; + while (y->m_next) { + z = y; + y = y->m_next; + } + + if (z->m_len + y->m_len > 2) { + int index = y->m_len - 1; + + c = *(mtod(y, char *) + index--); + switch (index) { + case -1: + y = z; + index = y->m_len - 1; + b = *(mtod(y, char *) + index--); + break; + case 0: + b = *(mtod(y, char *) + index--); + y = z; + index = y->m_len - 1; + break; + default: + b = *(mtod(y, char *) + index--); + break; + } + a = *(mtod(y, char *) + index--); + } else { + int begin = 0; + int end = so->so_rcv.sb_cc - 3; + + y = so->so_rcv.sb_mb; + a = sbindex(&y, &begin, end++); + b = sbindex(&y, &begin, end++); + c = sbindex(&y, &begin, end++); + } + + if (c == '\n' && (b == '\n' || (b == '\r' && a == '\n'))) { + /* we have all request headers */ + goto done; + } else { + /* still need more data */ + so->so_upcall = soishttpconnected; + so->so_rcv.sb_flags |= SB_UPCALL; + return; + } + } + +done: + so->so_upcall = NULL; + so->so_rcv.sb_flags &= ~SB_UPCALL; + soisconnected(so); + return; +} + +static void +sohasdata(struct socket *so, void *arg, int waitflag) +{ + + if (!soreadable(so)) { + return; + } + + so->so_upcall = NULL; + so->so_rcv.sb_flags &= ~SB_UPCALL; + soisconnected(so); + return; +} + void soisconnected(so) - register struct socket *so; + struct socket *so; { - register struct socket *head = so->so_head; + struct socket *head = so->so_head; + int s; so->so_state &= ~(SS_ISCONNECTING|SS_ISDISCONNECTING|SS_ISCONFIRMING); so->so_state |= SS_ISCONNECTED; if (head && (so->so_state & SS_INCOMP)) { + if ((so->so_options & (SO_DELAYACCEPT|SO_HTTPACCEPT)) != 0) { + s = splnet(); + if (!soreadable(so)) { + if ((so->so_options & SO_DELAYACCEPT) != 0) + so->so_upcall = sohasdata; + else + so->so_upcall = sohashttpgetorpost; + so->so_rcv.sb_flags |= SB_UPCALL; + splx(s); + return; + } + splx(s); + } TAILQ_REMOVE(&head->so_incomp, so, so_list); head->so_incqlen--; so->so_state &= ~SS_INCOMP; diff --git a/sys/sys/socket.h b/sys/sys/socket.h index b71e206c61c..630edcad1c4 100644 --- a/sys/sys/socket.h +++ b/sys/sys/socket.h @@ -70,6 +70,8 @@ typedef u_int32_t socklen_t; #define SO_OOBINLINE 0x0100 /* leave received OOB data in line */ #define SO_REUSEPORT 0x0200 /* allow local address & port reuse */ #define SO_TIMESTAMP 0x0400 /* timestamp received dgram traffic */ +#define SO_DELAYACCEPT 0x1000 /* delay accept until data arrived */ +#define SO_HTTPACCEPT 0x2000 /* delay accept until http request */ /* * Additional options, not kept in so_options.