Annotation of embedaddon/php/main/streams/xp_socket.c, revision 1.1.1.3

1.1       misho       1: /*
                      2:   +----------------------------------------------------------------------+
                      3:   | PHP Version 5                                                        |
                      4:   +----------------------------------------------------------------------+
1.1.1.3 ! misho       5:   | Copyright (c) 1997-2013 The PHP Group                                |
1.1       misho       6:   +----------------------------------------------------------------------+
                      7:   | This source file is subject to version 3.01 of the PHP license,      |
                      8:   | that is bundled with this package in the file LICENSE, and is        |
                      9:   | available through the world-wide-web at the following url:           |
                     10:   | http://www.php.net/license/3_01.txt                                  |
                     11:   | If you did not receive a copy of the PHP license and are unable to   |
                     12:   | obtain it through the world-wide-web, please send a note to          |
                     13:   | license@php.net so we can mail you a copy immediately.               |
                     14:   +----------------------------------------------------------------------+
                     15:   | Author: Wez Furlong <wez@thebrainroom.com>                           |
                     16:   +----------------------------------------------------------------------+
                     17: */
                     18: 
1.1.1.2   misho      19: /* $Id$ */
1.1       misho      20: 
                     21: #include "php.h"
                     22: #include "ext/standard/file.h"
                     23: #include "streams/php_streams_int.h"
                     24: #include "php_network.h"
                     25: 
                     26: #if defined(PHP_WIN32) || defined(__riscos__) || defined(NETWARE)
                     27: # undef AF_UNIX
                     28: #endif
                     29: 
                     30: #if defined(AF_UNIX)
                     31: #include <sys/un.h>
                     32: #endif
                     33: 
                     34: #ifndef MSG_DONTWAIT
                     35: # define MSG_DONTWAIT 0
                     36: #endif
                     37: 
                     38: #ifndef MSG_PEEK
                     39: # define MSG_PEEK 0
                     40: #endif
                     41: 
                     42: php_stream_ops php_stream_generic_socket_ops;
                     43: PHPAPI php_stream_ops php_stream_socket_ops;
                     44: php_stream_ops php_stream_udp_socket_ops;
                     45: #ifdef AF_UNIX
                     46: php_stream_ops php_stream_unix_socket_ops;
                     47: php_stream_ops php_stream_unixdg_socket_ops;
                     48: #endif
                     49: 
                     50: 
                     51: static int php_tcp_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC);
                     52: 
                     53: /* {{{ Generic socket stream operations */
                     54: static size_t php_sockop_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC)
                     55: {
                     56:        php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
                     57:        int didwrite;
                     58:        struct timeval *ptimeout;
                     59: 
                     60:        if (sock->socket == -1) {
                     61:                return 0;
                     62:        }
                     63: 
                     64:        if (sock->timeout.tv_sec == -1)
                     65:                ptimeout = NULL;
                     66:        else
                     67:                ptimeout = &sock->timeout;
                     68: 
                     69: retry:
                     70:        didwrite = send(sock->socket, buf, count, (sock->is_blocked && ptimeout) ? MSG_DONTWAIT : 0);
                     71: 
                     72:        if (didwrite <= 0) {
                     73:                long err = php_socket_errno();
                     74:                char *estr;
                     75: 
                     76:                if (sock->is_blocked && err == EWOULDBLOCK) {
                     77:                        int retval;
                     78: 
                     79:                        sock->timeout_event = 0;
                     80: 
                     81:                        do {
                     82:                                retval = php_pollfd_for(sock->socket, POLLOUT, ptimeout);
                     83: 
                     84:                                if (retval == 0) {
                     85:                                        sock->timeout_event = 1;
                     86:                                        break;
                     87:                                }
                     88: 
                     89:                                if (retval > 0) {
                     90:                                        /* writable now; retry */
                     91:                                        goto retry;
                     92:                                }
                     93: 
                     94:                                err = php_socket_errno();
                     95:                        } while (err == EINTR);
                     96:                }
                     97:                estr = php_socket_strerror(err, NULL, 0);
                     98:                php_error_docref(NULL TSRMLS_CC, E_NOTICE, "send of %ld bytes failed with errno=%ld %s",
                     99:                                (long)count, err, estr);
                    100:                efree(estr);
                    101:        }
                    102: 
                    103:        if (didwrite > 0) {
                    104:                php_stream_notify_progress_increment(stream->context, didwrite, 0);
                    105:        }
                    106: 
                    107:        if (didwrite < 0) {
                    108:                didwrite = 0;
                    109:        }
                    110: 
                    111:        return didwrite;
                    112: }
                    113: 
                    114: static void php_sock_stream_wait_for_data(php_stream *stream, php_netstream_data_t *sock TSRMLS_DC)
                    115: {
                    116:        int retval;
                    117:        struct timeval *ptimeout;
                    118: 
                    119:        if (sock->socket == -1) {
                    120:                return;
                    121:        }
                    122:        
                    123:        sock->timeout_event = 0;
                    124: 
                    125:        if (sock->timeout.tv_sec == -1)
                    126:                ptimeout = NULL;
                    127:        else
                    128:                ptimeout = &sock->timeout;
                    129: 
                    130:        while(1) {
                    131:                retval = php_pollfd_for(sock->socket, PHP_POLLREADABLE, ptimeout);
                    132: 
                    133:                if (retval == 0)
                    134:                        sock->timeout_event = 1;
                    135: 
                    136:                if (retval >= 0)
                    137:                        break;
                    138: 
                    139:                if (php_socket_errno() != EINTR)
                    140:                        break;
                    141:        }
                    142: }
                    143: 
                    144: static size_t php_sockop_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
                    145: {
                    146:        php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
                    147:        int nr_bytes = 0;
                    148: 
                    149:        if (sock->socket == -1) {
                    150:                return 0;
                    151:        }
                    152: 
                    153:        if (sock->is_blocked) {
                    154:                php_sock_stream_wait_for_data(stream, sock TSRMLS_CC);
                    155:                if (sock->timeout_event)
                    156:                        return 0;
                    157:        }
                    158: 
                    159:        nr_bytes = recv(sock->socket, buf, count, (sock->is_blocked && sock->timeout.tv_sec != -1) ? MSG_DONTWAIT : 0);
                    160: 
                    161:        stream->eof = (nr_bytes == 0 || (nr_bytes == -1 && php_socket_errno() != EWOULDBLOCK));
                    162: 
                    163:        if (nr_bytes > 0) {
                    164:                php_stream_notify_progress_increment(stream->context, nr_bytes, 0);
                    165:        }
                    166: 
                    167:        if (nr_bytes < 0) {
                    168:                nr_bytes = 0;
                    169:        }
                    170: 
                    171:        return nr_bytes;
                    172: }
                    173: 
                    174: 
                    175: static int php_sockop_close(php_stream *stream, int close_handle TSRMLS_DC)
                    176: {
                    177:        php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
                    178: #ifdef PHP_WIN32
                    179:        int n;
                    180: #endif
                    181: 
                    182:        if (close_handle) {
                    183: 
                    184: #ifdef PHP_WIN32
                    185:                if (sock->socket == -1)
                    186:                        sock->socket = SOCK_ERR;
                    187: #endif
                    188:                if (sock->socket != SOCK_ERR) {
                    189: #ifdef PHP_WIN32
                    190:                        /* prevent more data from coming in */
                    191:                        shutdown(sock->socket, SHUT_RD);
                    192: 
                    193:                        /* try to make sure that the OS sends all data before we close the connection.
                    194:                         * Essentially, we are waiting for the socket to become writeable, which means
                    195:                         * that all pending data has been sent.
                    196:                         * We use a small timeout which should encourage the OS to send the data,
1.1.1.3 ! misho     197:                         * but at the same time avoid hanging indefinitely.
1.1       misho     198:                         * */
                    199:                        do {
                    200:                                n = php_pollfd_for_ms(sock->socket, POLLOUT, 500);
                    201:                        } while (n == -1 && php_socket_errno() == EINTR);
                    202: #endif
                    203:                        closesocket(sock->socket);
                    204:                        sock->socket = SOCK_ERR;
                    205:                }
                    206: 
                    207:        }
                    208: 
                    209:        pefree(sock, php_stream_is_persistent(stream));
                    210:        
                    211:        return 0;
                    212: }
                    213: 
                    214: static int php_sockop_flush(php_stream *stream TSRMLS_DC)
                    215: {
                    216: #if 0
                    217:        php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
                    218:        return fsync(sock->socket);
                    219: #endif
                    220:        return 0;
                    221: }
                    222: 
                    223: static int php_sockop_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC)
                    224: {
                    225:        php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
                    226: #if ZEND_WIN32
                    227:        return 0;
                    228: #else
                    229:        return fstat(sock->socket, &ssb->sb);
                    230: #endif
                    231: }
                    232: 
                    233: static inline int sock_sendto(php_netstream_data_t *sock, char *buf, size_t buflen, int flags,
                    234:                struct sockaddr *addr, socklen_t addrlen
                    235:                TSRMLS_DC)
                    236: {
                    237:        int ret;
                    238:        if (addr) {
                    239:                ret = sendto(sock->socket, buf, buflen, flags, addr, addrlen);
                    240:                return (ret == SOCK_CONN_ERR) ? -1 : ret;
                    241:        }
                    242:        return ((ret = send(sock->socket, buf, buflen, flags)) == SOCK_CONN_ERR) ? -1 : ret;
                    243: }
                    244: 
                    245: static inline int sock_recvfrom(php_netstream_data_t *sock, char *buf, size_t buflen, int flags,
                    246:                char **textaddr, long *textaddrlen,
                    247:                struct sockaddr **addr, socklen_t *addrlen
                    248:                TSRMLS_DC)
                    249: {
                    250:        php_sockaddr_storage sa;
                    251:        socklen_t sl = sizeof(sa);
                    252:        int ret;
                    253:        int want_addr = textaddr || addr;
                    254: 
                    255:        if (want_addr) {
                    256:                ret = recvfrom(sock->socket, buf, buflen, flags, (struct sockaddr*)&sa, &sl);
                    257:                ret = (ret == SOCK_CONN_ERR) ? -1 : ret;
                    258:                php_network_populate_name_from_sockaddr((struct sockaddr*)&sa, sl,
                    259:                        textaddr, textaddrlen, addr, addrlen TSRMLS_CC);
                    260:        } else {
                    261:                ret = recv(sock->socket, buf, buflen, flags);
                    262:                ret = (ret == SOCK_CONN_ERR) ? -1 : ret;
                    263:        }
                    264: 
                    265:        return ret;
                    266: }
                    267: 
                    268: static int php_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC)
                    269: {
                    270:        int oldmode, flags;
                    271:        php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
                    272:        php_stream_xport_param *xparam;
                    273:        
                    274:        switch(option) {
                    275:                case PHP_STREAM_OPTION_CHECK_LIVENESS:
                    276:                        {
                    277:                                struct timeval tv;
                    278:                                char buf;
                    279:                                int alive = 1;
                    280: 
                    281:                                if (value == -1) {
                    282:                                        if (sock->timeout.tv_sec == -1) {
                    283:                                                tv.tv_sec = FG(default_socket_timeout);
                    284:                                                tv.tv_usec = 0;
                    285:                                        } else {
                    286:                                                tv = sock->timeout;
                    287:                                        }
                    288:                                } else {
                    289:                                        tv.tv_sec = value;
                    290:                                        tv.tv_usec = 0;
                    291:                                }
                    292: 
                    293:                                if (sock->socket == -1) {
                    294:                                        alive = 0;
                    295:                                } else if (php_pollfd_for(sock->socket, PHP_POLLREADABLE|POLLPRI, &tv) > 0) {
                    296:                                        if (0 >= recv(sock->socket, &buf, sizeof(buf), MSG_PEEK) && php_socket_errno() != EWOULDBLOCK) {
                    297:                                                alive = 0;
                    298:                                        }
                    299:                                }
                    300:                                return alive ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR;
                    301:                        }
                    302:                        
                    303:                case PHP_STREAM_OPTION_BLOCKING:
                    304:                        oldmode = sock->is_blocked;
                    305:                        if (SUCCESS == php_set_sock_blocking(sock->socket, value TSRMLS_CC)) {
                    306:                                sock->is_blocked = value;
                    307:                                return oldmode;
                    308:                        }
                    309:                        return PHP_STREAM_OPTION_RETURN_ERR;
                    310: 
                    311:                case PHP_STREAM_OPTION_READ_TIMEOUT:
                    312:                        sock->timeout = *(struct timeval*)ptrparam;
                    313:                        sock->timeout_event = 0;
                    314:                        return PHP_STREAM_OPTION_RETURN_OK;
                    315: 
                    316:                case PHP_STREAM_OPTION_META_DATA_API:
                    317:                        add_assoc_bool((zval *)ptrparam, "timed_out", sock->timeout_event);
                    318:                        add_assoc_bool((zval *)ptrparam, "blocked", sock->is_blocked);
                    319:                        add_assoc_bool((zval *)ptrparam, "eof", stream->eof);
                    320:                        return PHP_STREAM_OPTION_RETURN_OK;
                    321:                
                    322:                case PHP_STREAM_OPTION_XPORT_API:
                    323:                        xparam = (php_stream_xport_param *)ptrparam;
                    324: 
                    325:                        switch (xparam->op) {
                    326:                                case STREAM_XPORT_OP_LISTEN:
                    327:                                        xparam->outputs.returncode = (listen(sock->socket, xparam->inputs.backlog) == 0) ?  0: -1;
                    328:                                        return PHP_STREAM_OPTION_RETURN_OK;
                    329: 
                    330:                                case STREAM_XPORT_OP_GET_NAME:
                    331:                                        xparam->outputs.returncode = php_network_get_sock_name(sock->socket,
                    332:                                                        xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
                    333:                                                        xparam->want_textaddr ? &xparam->outputs.textaddrlen : NULL,
                    334:                                                        xparam->want_addr ? &xparam->outputs.addr : NULL,
                    335:                                                        xparam->want_addr ? &xparam->outputs.addrlen : NULL
                    336:                                                        TSRMLS_CC);
                    337:                                        return PHP_STREAM_OPTION_RETURN_OK;
                    338: 
                    339:                                case STREAM_XPORT_OP_GET_PEER_NAME:
                    340:                                        xparam->outputs.returncode = php_network_get_peer_name(sock->socket,
                    341:                                                        xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
                    342:                                                        xparam->want_textaddr ? &xparam->outputs.textaddrlen : NULL,
                    343:                                                        xparam->want_addr ? &xparam->outputs.addr : NULL,
                    344:                                                        xparam->want_addr ? &xparam->outputs.addrlen : NULL
                    345:                                                        TSRMLS_CC);
                    346:                                        return PHP_STREAM_OPTION_RETURN_OK;
                    347: 
                    348:                                case STREAM_XPORT_OP_SEND:
                    349:                                        flags = 0;
                    350:                                        if ((xparam->inputs.flags & STREAM_OOB) == STREAM_OOB) {
                    351:                                                flags |= MSG_OOB;
                    352:                                        }
                    353:                                        xparam->outputs.returncode = sock_sendto(sock,
                    354:                                                        xparam->inputs.buf, xparam->inputs.buflen,
                    355:                                                        flags,
                    356:                                                        xparam->inputs.addr,
                    357:                                                        xparam->inputs.addrlen TSRMLS_CC);
                    358:                                        if (xparam->outputs.returncode == -1) {
                    359:                                                char *err = php_socket_strerror(php_socket_errno(), NULL, 0);
                    360:                                                php_error_docref(NULL TSRMLS_CC, E_WARNING,
                    361:                                                        "%s\n", err);
                    362:                                                efree(err);
                    363:                                        }
                    364:                                        return PHP_STREAM_OPTION_RETURN_OK;
                    365: 
                    366:                                case STREAM_XPORT_OP_RECV:
                    367:                                        flags = 0;
                    368:                                        if ((xparam->inputs.flags & STREAM_OOB) == STREAM_OOB) {
                    369:                                                flags |= MSG_OOB;
                    370:                                        }
                    371:                                        if ((xparam->inputs.flags & STREAM_PEEK) == STREAM_PEEK) {
                    372:                                                flags |= MSG_PEEK;
                    373:                                        }
                    374:                                        xparam->outputs.returncode = sock_recvfrom(sock,
                    375:                                                        xparam->inputs.buf, xparam->inputs.buflen,
                    376:                                                        flags,
                    377:                                                        xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
                    378:                                                        xparam->want_textaddr ? &xparam->outputs.textaddrlen : NULL,
                    379:                                                        xparam->want_addr ? &xparam->outputs.addr : NULL,
                    380:                                                        xparam->want_addr ? &xparam->outputs.addrlen : NULL
                    381:                                                        TSRMLS_CC);
                    382:                                        return PHP_STREAM_OPTION_RETURN_OK;
                    383: 
                    384: 
                    385: #ifdef HAVE_SHUTDOWN
                    386: # ifndef SHUT_RD
                    387: #  define SHUT_RD 0
                    388: # endif
                    389: # ifndef SHUT_WR
                    390: #  define SHUT_WR 1
                    391: # endif
                    392: # ifndef SHUT_RDWR
                    393: #  define SHUT_RDWR 2
                    394: # endif
                    395:                                case STREAM_XPORT_OP_SHUTDOWN: {
                    396:                                        static const int shutdown_how[] = {SHUT_RD, SHUT_WR, SHUT_RDWR};
                    397: 
                    398:                                        xparam->outputs.returncode = shutdown(sock->socket, shutdown_how[xparam->how]);
                    399:                                        return PHP_STREAM_OPTION_RETURN_OK;
                    400:                                }
                    401: #endif
                    402:                                
                    403:                                default:
                    404:                                        return PHP_STREAM_OPTION_RETURN_NOTIMPL;
                    405:                        }
                    406: 
                    407:                default:
                    408:                        return PHP_STREAM_OPTION_RETURN_NOTIMPL;
                    409:        }
                    410: }
                    411: 
                    412: static int php_sockop_cast(php_stream *stream, int castas, void **ret TSRMLS_DC)
                    413: {
                    414:        php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
                    415: 
                    416:        switch(castas)  {
                    417:                case PHP_STREAM_AS_STDIO:
                    418:                        if (ret)        {
                    419:                                *(FILE**)ret = fdopen(sock->socket, stream->mode);
                    420:                                if (*ret)
                    421:                                        return SUCCESS;
                    422:                                return FAILURE;
                    423:                        }
                    424:                        return SUCCESS;
                    425:                case PHP_STREAM_AS_FD_FOR_SELECT:
                    426:                case PHP_STREAM_AS_FD:
                    427:                case PHP_STREAM_AS_SOCKETD:
                    428:                        if (ret)
                    429:                                *(int*)ret = sock->socket;
                    430:                        return SUCCESS;
                    431:                default:
                    432:                        return FAILURE;
                    433:        }
                    434: }
                    435: /* }}} */
                    436: 
                    437: /* These may look identical, but we need them this way so that
                    438:  * we can determine which type of socket we are dealing with
                    439:  * by inspecting stream->ops.
                    440:  * A "useful" side-effect is that the user's scripts can then
                    441:  * make similar decisions using stream_get_meta_data.
                    442:  * */
                    443: php_stream_ops php_stream_generic_socket_ops = {
                    444:        php_sockop_write, php_sockop_read,
                    445:        php_sockop_close, php_sockop_flush,
                    446:        "generic_socket",
                    447:        NULL, /* seek */
                    448:        php_sockop_cast,
                    449:        php_sockop_stat,
                    450:        php_sockop_set_option,
                    451: };
                    452: 
                    453: 
                    454: php_stream_ops php_stream_socket_ops = {
                    455:        php_sockop_write, php_sockop_read,
                    456:        php_sockop_close, php_sockop_flush,
                    457:        "tcp_socket",
                    458:        NULL, /* seek */
                    459:        php_sockop_cast,
                    460:        php_sockop_stat,
                    461:        php_tcp_sockop_set_option,
                    462: };
                    463: 
                    464: php_stream_ops php_stream_udp_socket_ops = {
                    465:        php_sockop_write, php_sockop_read,
                    466:        php_sockop_close, php_sockop_flush,
                    467:        "udp_socket",
                    468:        NULL, /* seek */
                    469:        php_sockop_cast,
                    470:        php_sockop_stat,
                    471:        php_tcp_sockop_set_option,
                    472: };
                    473: 
                    474: #ifdef AF_UNIX
                    475: php_stream_ops php_stream_unix_socket_ops = {
                    476:        php_sockop_write, php_sockop_read,
                    477:        php_sockop_close, php_sockop_flush,
                    478:        "unix_socket",
                    479:        NULL, /* seek */
                    480:        php_sockop_cast,
                    481:        php_sockop_stat,
                    482:        php_tcp_sockop_set_option,
                    483: };
                    484: php_stream_ops php_stream_unixdg_socket_ops = {
                    485:        php_sockop_write, php_sockop_read,
                    486:        php_sockop_close, php_sockop_flush,
                    487:        "udg_socket",
                    488:        NULL, /* seek */
                    489:        php_sockop_cast,
                    490:        php_sockop_stat,
                    491:        php_tcp_sockop_set_option,
                    492: };
                    493: #endif
                    494: 
                    495: 
                    496: /* network socket operations */
                    497: 
                    498: #ifdef AF_UNIX
                    499: static inline int parse_unix_address(php_stream_xport_param *xparam, struct sockaddr_un *unix_addr TSRMLS_DC)
                    500: {
                    501:        memset(unix_addr, 0, sizeof(*unix_addr));
                    502:        unix_addr->sun_family = AF_UNIX;
                    503: 
                    504:        /* we need to be binary safe on systems that support an abstract
                    505:         * namespace */
                    506:        if (xparam->inputs.namelen >= sizeof(unix_addr->sun_path)) {
                    507:                /* On linux, when the path begins with a NUL byte we are
                    508:                 * referring to an abstract namespace.  In theory we should
                    509:                 * allow an extra byte below, since we don't need the NULL.
                    510:                 * BUT, to get into this branch of code, the name is too long,
                    511:                 * so we don't care. */
                    512:                xparam->inputs.namelen = sizeof(unix_addr->sun_path) - 1;
1.1.1.2   misho     513:                php_error_docref(NULL TSRMLS_CC, E_NOTICE,
                    514:                        "socket path exceeded the maximum allowed length of %lu bytes "
                    515:                        "and was truncated", (unsigned long)sizeof(unix_addr->sun_path));
1.1       misho     516:        }
                    517: 
                    518:        memcpy(unix_addr->sun_path, xparam->inputs.name, xparam->inputs.namelen);
                    519: 
                    520:        return 1;
                    521: }
                    522: #endif
                    523: 
                    524: static inline char *parse_ip_address_ex(const char *str, int str_len, int *portno, int get_err, char **err TSRMLS_DC)
                    525: {
                    526:        char *colon;
                    527:        char *host = NULL;
                    528: 
                    529: #ifdef HAVE_IPV6
                    530:        char *p;
                    531: 
                    532:        if (*(str) == '[' && str_len > 1) {
                    533:                /* IPV6 notation to specify raw address with port (i.e. [fe80::1]:80) */
                    534:                p = memchr(str + 1, ']', str_len - 2);
                    535:                if (!p || *(p + 1) != ':') {
                    536:                        if (get_err) {
                    537:                                spprintf(err, 0, "Failed to parse IPv6 address \"%s\"", str);
                    538:                        }
                    539:                        return NULL;
                    540:                }
                    541:                *portno = atoi(p + 2);
                    542:                return estrndup(str + 1, p - str - 1);
                    543:        }
                    544: #endif
                    545:        if (str_len) {
                    546:                colon = memchr(str, ':', str_len - 1);
                    547:        } else {
                    548:                colon = NULL;
                    549:        }
                    550:        if (colon) {
                    551:                *portno = atoi(colon + 1);
                    552:                host = estrndup(str, colon - str);
                    553:        } else {
                    554:                if (get_err) {
                    555:                        spprintf(err, 0, "Failed to parse address \"%s\"", str);
                    556:                }
                    557:                return NULL;
                    558:        }
                    559: 
                    560:        return host;
                    561: }
                    562: 
                    563: static inline char *parse_ip_address(php_stream_xport_param *xparam, int *portno TSRMLS_DC)
                    564: {
                    565:        return parse_ip_address_ex(xparam->inputs.name, xparam->inputs.namelen, portno, xparam->want_errortext, &xparam->outputs.error_text TSRMLS_CC);
                    566: }
                    567: 
                    568: static inline int php_tcp_sockop_bind(php_stream *stream, php_netstream_data_t *sock,
                    569:                php_stream_xport_param *xparam TSRMLS_DC)
                    570: {
                    571:        char *host = NULL;
                    572:        int portno, err;
                    573: 
                    574: #ifdef AF_UNIX
                    575:        if (stream->ops == &php_stream_unix_socket_ops || stream->ops == &php_stream_unixdg_socket_ops) {
                    576:                struct sockaddr_un unix_addr;
                    577: 
                    578:                sock->socket = socket(PF_UNIX, stream->ops == &php_stream_unix_socket_ops ? SOCK_STREAM : SOCK_DGRAM, 0);
                    579: 
                    580:                if (sock->socket == SOCK_ERR) {
                    581:                        if (xparam->want_errortext) {
                    582:                                spprintf(&xparam->outputs.error_text, 0, "Failed to create unix%s socket %s",
                    583:                                                stream->ops == &php_stream_unix_socket_ops ? "" : "datagram",
                    584:                                                strerror(errno));
                    585:                        }
                    586:                        return -1;
                    587:                }
                    588: 
                    589:                parse_unix_address(xparam, &unix_addr TSRMLS_CC);
                    590: 
                    591:                return bind(sock->socket, (struct sockaddr *)&unix_addr, sizeof(unix_addr));
                    592:        }
                    593: #endif
                    594: 
                    595:        host = parse_ip_address(xparam, &portno TSRMLS_CC);
                    596: 
                    597:        if (host == NULL) {
                    598:                return -1;
                    599:        }
                    600: 
                    601:        sock->socket = php_network_bind_socket_to_local_addr(host, portno,
                    602:                        stream->ops == &php_stream_udp_socket_ops ? SOCK_DGRAM : SOCK_STREAM,
                    603:                        xparam->want_errortext ? &xparam->outputs.error_text : NULL,
                    604:                        &err
                    605:                        TSRMLS_CC);
                    606:        
                    607:        if (host) {
                    608:                efree(host);
                    609:        }
                    610: 
                    611:        return sock->socket == -1 ? -1 : 0;
                    612: }
                    613: 
                    614: static inline int php_tcp_sockop_connect(php_stream *stream, php_netstream_data_t *sock,
                    615:                php_stream_xport_param *xparam TSRMLS_DC)
                    616: {
                    617:        char *host = NULL, *bindto = NULL;
                    618:        int portno, bindport = 0;
                    619:        int err = 0;
                    620:        int ret;
                    621:        zval **tmpzval = NULL;
                    622: 
                    623: #ifdef AF_UNIX
                    624:        if (stream->ops == &php_stream_unix_socket_ops || stream->ops == &php_stream_unixdg_socket_ops) {
                    625:                struct sockaddr_un unix_addr;
                    626: 
                    627:                sock->socket = socket(PF_UNIX, stream->ops == &php_stream_unix_socket_ops ? SOCK_STREAM : SOCK_DGRAM, 0);
                    628: 
                    629:                if (sock->socket == SOCK_ERR) {
                    630:                        if (xparam->want_errortext) {
                    631:                                spprintf(&xparam->outputs.error_text, 0, "Failed to create unix socket");
                    632:                        }
                    633:                        return -1;
                    634:                }
                    635: 
                    636:                parse_unix_address(xparam, &unix_addr TSRMLS_CC);
                    637: 
                    638:                ret = php_network_connect_socket(sock->socket,
                    639:                                (const struct sockaddr *)&unix_addr, (socklen_t) XtOffsetOf(struct sockaddr_un, sun_path) + xparam->inputs.namelen,
                    640:                                xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC, xparam->inputs.timeout,
                    641:                                xparam->want_errortext ? &xparam->outputs.error_text : NULL,
                    642:                                &err);
                    643: 
                    644:                xparam->outputs.error_code = err;
                    645: 
                    646:                goto out;
                    647:        }
                    648: #endif
                    649: 
                    650:        host = parse_ip_address(xparam, &portno TSRMLS_CC);
                    651: 
                    652:        if (host == NULL) {
                    653:                return -1;
                    654:        }
                    655: 
                    656:        if (stream->context && php_stream_context_get_option(stream->context, "socket", "bindto", &tmpzval) == SUCCESS) {
                    657:                if (Z_TYPE_PP(tmpzval) != IS_STRING) {
                    658:                        if (xparam->want_errortext) {
                    659:                                spprintf(&xparam->outputs.error_text, 0, "local_addr context option is not a string.");
                    660:                        }
                    661:                        efree(host);
                    662:                        return -1;
                    663:                }
                    664:                bindto = parse_ip_address_ex(Z_STRVAL_PP(tmpzval), Z_STRLEN_PP(tmpzval), &bindport, xparam->want_errortext, &xparam->outputs.error_text TSRMLS_CC);
                    665:        }
                    666: 
                    667:        /* Note: the test here for php_stream_udp_socket_ops is important, because we
                    668:         * want the default to be TCP sockets so that the openssl extension can
                    669:         * re-use this code. */
                    670:        
                    671:        sock->socket = php_network_connect_socket_to_host(host, portno,
                    672:                        stream->ops == &php_stream_udp_socket_ops ? SOCK_DGRAM : SOCK_STREAM,
                    673:                        xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC,
                    674:                        xparam->inputs.timeout,
                    675:                        xparam->want_errortext ? &xparam->outputs.error_text : NULL,
                    676:                        &err,
                    677:                        bindto,
                    678:                        bindport
                    679:                        TSRMLS_CC);
                    680:        
                    681:        ret = sock->socket == -1 ? -1 : 0;
                    682:        xparam->outputs.error_code = err;
                    683: 
                    684:        if (host) {
                    685:                efree(host);
                    686:        }
                    687:        if (bindto) {
                    688:                efree(bindto);
                    689:        }
                    690: 
                    691: #ifdef AF_UNIX
                    692: out:
                    693: #endif
                    694: 
                    695:        if (ret >= 0 && xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC && err == EINPROGRESS) {
                    696:                /* indicates pending connection */
                    697:                return 1;
                    698:        }
                    699:        
                    700:        return ret;
                    701: }
                    702: 
                    703: static inline int php_tcp_sockop_accept(php_stream *stream, php_netstream_data_t *sock,
                    704:                php_stream_xport_param *xparam STREAMS_DC TSRMLS_DC)
                    705: {
                    706:        int clisock;
                    707: 
                    708:        xparam->outputs.client = NULL;
                    709: 
                    710:        clisock = php_network_accept_incoming(sock->socket,
                    711:                        xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
                    712:                        xparam->want_textaddr ? &xparam->outputs.textaddrlen : NULL,
                    713:                        xparam->want_addr ? &xparam->outputs.addr : NULL,
                    714:                        xparam->want_addr ? &xparam->outputs.addrlen : NULL,
                    715:                        xparam->inputs.timeout,
                    716:                        xparam->want_errortext ? &xparam->outputs.error_text : NULL,
                    717:                        &xparam->outputs.error_code
                    718:                        TSRMLS_CC);
                    719: 
                    720:        if (clisock >= 0) {
                    721:                php_netstream_data_t *clisockdata;
                    722: 
                    723:                clisockdata = emalloc(sizeof(*clisockdata));
                    724: 
                    725:                if (clisockdata == NULL) {
                    726:                        close(clisock);
                    727:                        /* technically a fatal error */
                    728:                } else {
                    729:                        memcpy(clisockdata, sock, sizeof(*clisockdata));
                    730:                        clisockdata->socket = clisock;
                    731: 
                    732:                        xparam->outputs.client = php_stream_alloc_rel(stream->ops, clisockdata, NULL, "r+");
                    733:                        if (xparam->outputs.client) {
                    734:                                xparam->outputs.client->context = stream->context;
                    735:                                if (stream->context) {
                    736:                                        zend_list_addref(stream->context->rsrc_id);
                    737:                                }
                    738:                        }
                    739:                }
                    740:        }
                    741:        
                    742:        return xparam->outputs.client == NULL ? -1 : 0;
                    743: }
                    744: 
                    745: static int php_tcp_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC)
                    746: {
                    747:        php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
                    748:        php_stream_xport_param *xparam;
                    749: 
                    750:        switch(option) {
                    751:                case PHP_STREAM_OPTION_XPORT_API:
                    752:                        xparam = (php_stream_xport_param *)ptrparam;
                    753: 
                    754:                        switch(xparam->op) {
                    755:                                case STREAM_XPORT_OP_CONNECT:
                    756:                                case STREAM_XPORT_OP_CONNECT_ASYNC:
                    757:                                        xparam->outputs.returncode = php_tcp_sockop_connect(stream, sock, xparam TSRMLS_CC);
                    758:                                        return PHP_STREAM_OPTION_RETURN_OK;
                    759: 
                    760:                                case STREAM_XPORT_OP_BIND:
                    761:                                        xparam->outputs.returncode = php_tcp_sockop_bind(stream, sock, xparam TSRMLS_CC);
                    762:                                        return PHP_STREAM_OPTION_RETURN_OK;
                    763: 
                    764: 
                    765:                                case STREAM_XPORT_OP_ACCEPT:
                    766:                                        xparam->outputs.returncode = php_tcp_sockop_accept(stream, sock, xparam STREAMS_CC TSRMLS_CC);
                    767:                                        return PHP_STREAM_OPTION_RETURN_OK;
                    768:                                default:
                    769:                                        /* fall through */
                    770:                                        ;
                    771:                        }
                    772:        }
                    773:        return php_sockop_set_option(stream, option, value, ptrparam TSRMLS_CC);
                    774: }
                    775: 
                    776: 
                    777: PHPAPI php_stream *php_stream_generic_socket_factory(const char *proto, long protolen,
                    778:                char *resourcename, long resourcenamelen,
                    779:                const char *persistent_id, int options, int flags,
                    780:                struct timeval *timeout,
                    781:                php_stream_context *context STREAMS_DC TSRMLS_DC)
                    782: {
                    783:        php_stream *stream = NULL;
                    784:        php_netstream_data_t *sock;
                    785:        php_stream_ops *ops;
                    786: 
                    787:        /* which type of socket ? */
                    788:        if (strncmp(proto, "tcp", protolen) == 0) {
                    789:                ops = &php_stream_socket_ops;
                    790:        } else if (strncmp(proto, "udp", protolen) == 0) {
                    791:                ops = &php_stream_udp_socket_ops;
                    792:        }
                    793: #ifdef AF_UNIX
                    794:        else if (strncmp(proto, "unix", protolen) == 0) {
                    795:                ops = &php_stream_unix_socket_ops;
                    796:        } else if (strncmp(proto, "udg", protolen) == 0) {
                    797:                ops = &php_stream_unixdg_socket_ops;
                    798:        }
                    799: #endif
                    800:        else {
                    801:                /* should never happen */
                    802:                return NULL;
                    803:        }
                    804:        
                    805:        sock = pemalloc(sizeof(php_netstream_data_t), persistent_id ? 1 : 0);
                    806:        memset(sock, 0, sizeof(php_netstream_data_t));
                    807: 
                    808:        sock->is_blocked = 1;
                    809:        sock->timeout.tv_sec = FG(default_socket_timeout);
                    810:        sock->timeout.tv_usec = 0;
                    811: 
                    812:        /* we don't know the socket until we have determined if we are binding or
                    813:         * connecting */
                    814:        sock->socket = -1;
                    815:        
                    816:        stream = php_stream_alloc_rel(ops, sock, persistent_id, "r+");
                    817: 
                    818:        if (stream == NULL)     {
                    819:                pefree(sock, persistent_id ? 1 : 0);
                    820:                return NULL;
                    821:        }
                    822: 
                    823:        if (flags == 0) {
                    824:                return stream;
                    825:        }
                    826: 
                    827:        return stream;
                    828: }
                    829: 
                    830: 
                    831: /*
                    832:  * Local variables:
                    833:  * tab-width: 4
                    834:  * c-basic-offset: 4
                    835:  * End:
                    836:  * vim600: noet sw=4 ts=4 fdm=marker
                    837:  * vim<600: noet sw=4 ts=4
                    838:  */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>