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

1.1     ! misho       1: /*
        !             2:   +----------------------------------------------------------------------+
        !             3:   | PHP Version 5                                                        |
        !             4:   +----------------------------------------------------------------------+
        !             5:   | Copyright (c) 1997-2012 The PHP Group                                |
        !             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: 
        !            19: /* $Id: xp_socket.c 321634 2012-01-01 13:15:04Z felipe $ */
        !            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,
        !           197:                         * but at the same time avoid hanging indefintely.
        !           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:                                case PHP_STREAM_OPTION_WRITE_BUFFER:
        !           404:                                        php_stream_set_chunk_size(stream, (ptrparam ? *(size_t *)ptrparam : PHP_SOCK_CHUNK_SIZE));
        !           405:                                        return PHP_STREAM_OPTION_RETURN_OK;
        !           406: 
        !           407:                                default:
        !           408:                                        return PHP_STREAM_OPTION_RETURN_NOTIMPL;
        !           409:                        }
        !           410: 
        !           411:                default:
        !           412:                        return PHP_STREAM_OPTION_RETURN_NOTIMPL;
        !           413:        }
        !           414: }
        !           415: 
        !           416: static int php_sockop_cast(php_stream *stream, int castas, void **ret TSRMLS_DC)
        !           417: {
        !           418:        php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
        !           419: 
        !           420:        switch(castas)  {
        !           421:                case PHP_STREAM_AS_STDIO:
        !           422:                        if (ret)        {
        !           423:                                *(FILE**)ret = fdopen(sock->socket, stream->mode);
        !           424:                                if (*ret)
        !           425:                                        return SUCCESS;
        !           426:                                return FAILURE;
        !           427:                        }
        !           428:                        return SUCCESS;
        !           429:                case PHP_STREAM_AS_FD_FOR_SELECT:
        !           430:                case PHP_STREAM_AS_FD:
        !           431:                case PHP_STREAM_AS_SOCKETD:
        !           432:                        if (ret)
        !           433:                                *(int*)ret = sock->socket;
        !           434:                        return SUCCESS;
        !           435:                default:
        !           436:                        return FAILURE;
        !           437:        }
        !           438: }
        !           439: /* }}} */
        !           440: 
        !           441: /* These may look identical, but we need them this way so that
        !           442:  * we can determine which type of socket we are dealing with
        !           443:  * by inspecting stream->ops.
        !           444:  * A "useful" side-effect is that the user's scripts can then
        !           445:  * make similar decisions using stream_get_meta_data.
        !           446:  * */
        !           447: php_stream_ops php_stream_generic_socket_ops = {
        !           448:        php_sockop_write, php_sockop_read,
        !           449:        php_sockop_close, php_sockop_flush,
        !           450:        "generic_socket",
        !           451:        NULL, /* seek */
        !           452:        php_sockop_cast,
        !           453:        php_sockop_stat,
        !           454:        php_sockop_set_option,
        !           455: };
        !           456: 
        !           457: 
        !           458: php_stream_ops php_stream_socket_ops = {
        !           459:        php_sockop_write, php_sockop_read,
        !           460:        php_sockop_close, php_sockop_flush,
        !           461:        "tcp_socket",
        !           462:        NULL, /* seek */
        !           463:        php_sockop_cast,
        !           464:        php_sockop_stat,
        !           465:        php_tcp_sockop_set_option,
        !           466: };
        !           467: 
        !           468: php_stream_ops php_stream_udp_socket_ops = {
        !           469:        php_sockop_write, php_sockop_read,
        !           470:        php_sockop_close, php_sockop_flush,
        !           471:        "udp_socket",
        !           472:        NULL, /* seek */
        !           473:        php_sockop_cast,
        !           474:        php_sockop_stat,
        !           475:        php_tcp_sockop_set_option,
        !           476: };
        !           477: 
        !           478: #ifdef AF_UNIX
        !           479: php_stream_ops php_stream_unix_socket_ops = {
        !           480:        php_sockop_write, php_sockop_read,
        !           481:        php_sockop_close, php_sockop_flush,
        !           482:        "unix_socket",
        !           483:        NULL, /* seek */
        !           484:        php_sockop_cast,
        !           485:        php_sockop_stat,
        !           486:        php_tcp_sockop_set_option,
        !           487: };
        !           488: php_stream_ops php_stream_unixdg_socket_ops = {
        !           489:        php_sockop_write, php_sockop_read,
        !           490:        php_sockop_close, php_sockop_flush,
        !           491:        "udg_socket",
        !           492:        NULL, /* seek */
        !           493:        php_sockop_cast,
        !           494:        php_sockop_stat,
        !           495:        php_tcp_sockop_set_option,
        !           496: };
        !           497: #endif
        !           498: 
        !           499: 
        !           500: /* network socket operations */
        !           501: 
        !           502: #ifdef AF_UNIX
        !           503: static inline int parse_unix_address(php_stream_xport_param *xparam, struct sockaddr_un *unix_addr TSRMLS_DC)
        !           504: {
        !           505:        memset(unix_addr, 0, sizeof(*unix_addr));
        !           506:        unix_addr->sun_family = AF_UNIX;
        !           507: 
        !           508:        /* we need to be binary safe on systems that support an abstract
        !           509:         * namespace */
        !           510:        if (xparam->inputs.namelen >= sizeof(unix_addr->sun_path)) {
        !           511:                /* On linux, when the path begins with a NUL byte we are
        !           512:                 * referring to an abstract namespace.  In theory we should
        !           513:                 * allow an extra byte below, since we don't need the NULL.
        !           514:                 * BUT, to get into this branch of code, the name is too long,
        !           515:                 * so we don't care. */
        !           516:                xparam->inputs.namelen = sizeof(unix_addr->sun_path) - 1;
        !           517:        }
        !           518: 
        !           519:        memcpy(unix_addr->sun_path, xparam->inputs.name, xparam->inputs.namelen);
        !           520: 
        !           521:        return 1;
        !           522: }
        !           523: #endif
        !           524: 
        !           525: static inline char *parse_ip_address_ex(const char *str, int str_len, int *portno, int get_err, char **err TSRMLS_DC)
        !           526: {
        !           527:        char *colon;
        !           528:        char *host = NULL;
        !           529: 
        !           530: #ifdef HAVE_IPV6
        !           531:        char *p;
        !           532: 
        !           533:        if (*(str) == '[' && str_len > 1) {
        !           534:                /* IPV6 notation to specify raw address with port (i.e. [fe80::1]:80) */
        !           535:                p = memchr(str + 1, ']', str_len - 2);
        !           536:                if (!p || *(p + 1) != ':') {
        !           537:                        if (get_err) {
        !           538:                                spprintf(err, 0, "Failed to parse IPv6 address \"%s\"", str);
        !           539:                        }
        !           540:                        return NULL;
        !           541:                }
        !           542:                *portno = atoi(p + 2);
        !           543:                return estrndup(str + 1, p - str - 1);
        !           544:        }
        !           545: #endif
        !           546:        if (str_len) {
        !           547:                colon = memchr(str, ':', str_len - 1);
        !           548:        } else {
        !           549:                colon = NULL;
        !           550:        }
        !           551:        if (colon) {
        !           552:                *portno = atoi(colon + 1);
        !           553:                host = estrndup(str, colon - str);
        !           554:        } else {
        !           555:                if (get_err) {
        !           556:                        spprintf(err, 0, "Failed to parse address \"%s\"", str);
        !           557:                }
        !           558:                return NULL;
        !           559:        }
        !           560: 
        !           561:        return host;
        !           562: }
        !           563: 
        !           564: static inline char *parse_ip_address(php_stream_xport_param *xparam, int *portno TSRMLS_DC)
        !           565: {
        !           566:        return parse_ip_address_ex(xparam->inputs.name, xparam->inputs.namelen, portno, xparam->want_errortext, &xparam->outputs.error_text TSRMLS_CC);
        !           567: }
        !           568: 
        !           569: static inline int php_tcp_sockop_bind(php_stream *stream, php_netstream_data_t *sock,
        !           570:                php_stream_xport_param *xparam TSRMLS_DC)
        !           571: {
        !           572:        char *host = NULL;
        !           573:        int portno, err;
        !           574: 
        !           575: #ifdef AF_UNIX
        !           576:        if (stream->ops == &php_stream_unix_socket_ops || stream->ops == &php_stream_unixdg_socket_ops) {
        !           577:                struct sockaddr_un unix_addr;
        !           578: 
        !           579:                sock->socket = socket(PF_UNIX, stream->ops == &php_stream_unix_socket_ops ? SOCK_STREAM : SOCK_DGRAM, 0);
        !           580: 
        !           581:                if (sock->socket == SOCK_ERR) {
        !           582:                        if (xparam->want_errortext) {
        !           583:                                spprintf(&xparam->outputs.error_text, 0, "Failed to create unix%s socket %s",
        !           584:                                                stream->ops == &php_stream_unix_socket_ops ? "" : "datagram",
        !           585:                                                strerror(errno));
        !           586:                        }
        !           587:                        return -1;
        !           588:                }
        !           589: 
        !           590:                parse_unix_address(xparam, &unix_addr TSRMLS_CC);
        !           591: 
        !           592:                return bind(sock->socket, (struct sockaddr *)&unix_addr, sizeof(unix_addr));
        !           593:        }
        !           594: #endif
        !           595: 
        !           596:        host = parse_ip_address(xparam, &portno TSRMLS_CC);
        !           597: 
        !           598:        if (host == NULL) {
        !           599:                return -1;
        !           600:        }
        !           601: 
        !           602:        sock->socket = php_network_bind_socket_to_local_addr(host, portno,
        !           603:                        stream->ops == &php_stream_udp_socket_ops ? SOCK_DGRAM : SOCK_STREAM,
        !           604:                        xparam->want_errortext ? &xparam->outputs.error_text : NULL,
        !           605:                        &err
        !           606:                        TSRMLS_CC);
        !           607:        
        !           608:        if (host) {
        !           609:                efree(host);
        !           610:        }
        !           611: 
        !           612:        return sock->socket == -1 ? -1 : 0;
        !           613: }
        !           614: 
        !           615: static inline int php_tcp_sockop_connect(php_stream *stream, php_netstream_data_t *sock,
        !           616:                php_stream_xport_param *xparam TSRMLS_DC)
        !           617: {
        !           618:        char *host = NULL, *bindto = NULL;
        !           619:        int portno, bindport = 0;
        !           620:        int err = 0;
        !           621:        int ret;
        !           622:        zval **tmpzval = NULL;
        !           623: 
        !           624: #ifdef AF_UNIX
        !           625:        if (stream->ops == &php_stream_unix_socket_ops || stream->ops == &php_stream_unixdg_socket_ops) {
        !           626:                struct sockaddr_un unix_addr;
        !           627: 
        !           628:                sock->socket = socket(PF_UNIX, stream->ops == &php_stream_unix_socket_ops ? SOCK_STREAM : SOCK_DGRAM, 0);
        !           629: 
        !           630:                if (sock->socket == SOCK_ERR) {
        !           631:                        if (xparam->want_errortext) {
        !           632:                                spprintf(&xparam->outputs.error_text, 0, "Failed to create unix socket");
        !           633:                        }
        !           634:                        return -1;
        !           635:                }
        !           636: 
        !           637:                parse_unix_address(xparam, &unix_addr TSRMLS_CC);
        !           638: 
        !           639:                ret = php_network_connect_socket(sock->socket,
        !           640:                                (const struct sockaddr *)&unix_addr, (socklen_t) XtOffsetOf(struct sockaddr_un, sun_path) + xparam->inputs.namelen,
        !           641:                                xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC, xparam->inputs.timeout,
        !           642:                                xparam->want_errortext ? &xparam->outputs.error_text : NULL,
        !           643:                                &err);
        !           644: 
        !           645:                xparam->outputs.error_code = err;
        !           646: 
        !           647:                goto out;
        !           648:        }
        !           649: #endif
        !           650: 
        !           651:        host = parse_ip_address(xparam, &portno TSRMLS_CC);
        !           652: 
        !           653:        if (host == NULL) {
        !           654:                return -1;
        !           655:        }
        !           656: 
        !           657:        if (stream->context && php_stream_context_get_option(stream->context, "socket", "bindto", &tmpzval) == SUCCESS) {
        !           658:                if (Z_TYPE_PP(tmpzval) != IS_STRING) {
        !           659:                        if (xparam->want_errortext) {
        !           660:                                spprintf(&xparam->outputs.error_text, 0, "local_addr context option is not a string.");
        !           661:                        }
        !           662:                        efree(host);
        !           663:                        return -1;
        !           664:                }
        !           665:                bindto = parse_ip_address_ex(Z_STRVAL_PP(tmpzval), Z_STRLEN_PP(tmpzval), &bindport, xparam->want_errortext, &xparam->outputs.error_text TSRMLS_CC);
        !           666:        }
        !           667: 
        !           668:        /* Note: the test here for php_stream_udp_socket_ops is important, because we
        !           669:         * want the default to be TCP sockets so that the openssl extension can
        !           670:         * re-use this code. */
        !           671:        
        !           672:        sock->socket = php_network_connect_socket_to_host(host, portno,
        !           673:                        stream->ops == &php_stream_udp_socket_ops ? SOCK_DGRAM : SOCK_STREAM,
        !           674:                        xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC,
        !           675:                        xparam->inputs.timeout,
        !           676:                        xparam->want_errortext ? &xparam->outputs.error_text : NULL,
        !           677:                        &err,
        !           678:                        bindto,
        !           679:                        bindport
        !           680:                        TSRMLS_CC);
        !           681:        
        !           682:        ret = sock->socket == -1 ? -1 : 0;
        !           683:        xparam->outputs.error_code = err;
        !           684: 
        !           685:        if (host) {
        !           686:                efree(host);
        !           687:        }
        !           688:        if (bindto) {
        !           689:                efree(bindto);
        !           690:        }
        !           691: 
        !           692: #ifdef AF_UNIX
        !           693: out:
        !           694: #endif
        !           695: 
        !           696:        if (ret >= 0 && xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC && err == EINPROGRESS) {
        !           697:                /* indicates pending connection */
        !           698:                return 1;
        !           699:        }
        !           700:        
        !           701:        return ret;
        !           702: }
        !           703: 
        !           704: static inline int php_tcp_sockop_accept(php_stream *stream, php_netstream_data_t *sock,
        !           705:                php_stream_xport_param *xparam STREAMS_DC TSRMLS_DC)
        !           706: {
        !           707:        int clisock;
        !           708: 
        !           709:        xparam->outputs.client = NULL;
        !           710: 
        !           711:        clisock = php_network_accept_incoming(sock->socket,
        !           712:                        xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
        !           713:                        xparam->want_textaddr ? &xparam->outputs.textaddrlen : NULL,
        !           714:                        xparam->want_addr ? &xparam->outputs.addr : NULL,
        !           715:                        xparam->want_addr ? &xparam->outputs.addrlen : NULL,
        !           716:                        xparam->inputs.timeout,
        !           717:                        xparam->want_errortext ? &xparam->outputs.error_text : NULL,
        !           718:                        &xparam->outputs.error_code
        !           719:                        TSRMLS_CC);
        !           720: 
        !           721:        if (clisock >= 0) {
        !           722:                php_netstream_data_t *clisockdata;
        !           723: 
        !           724:                clisockdata = emalloc(sizeof(*clisockdata));
        !           725: 
        !           726:                if (clisockdata == NULL) {
        !           727:                        close(clisock);
        !           728:                        /* technically a fatal error */
        !           729:                } else {
        !           730:                        memcpy(clisockdata, sock, sizeof(*clisockdata));
        !           731:                        clisockdata->socket = clisock;
        !           732: 
        !           733:                        xparam->outputs.client = php_stream_alloc_rel(stream->ops, clisockdata, NULL, "r+");
        !           734:                        if (xparam->outputs.client) {
        !           735:                                xparam->outputs.client->context = stream->context;
        !           736:                                if (stream->context) {
        !           737:                                        zend_list_addref(stream->context->rsrc_id);
        !           738:                                }
        !           739:                        }
        !           740:                }
        !           741:        }
        !           742:        
        !           743:        return xparam->outputs.client == NULL ? -1 : 0;
        !           744: }
        !           745: 
        !           746: static int php_tcp_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC)
        !           747: {
        !           748:        php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract;
        !           749:        php_stream_xport_param *xparam;
        !           750: 
        !           751:        switch(option) {
        !           752:                case PHP_STREAM_OPTION_XPORT_API:
        !           753:                        xparam = (php_stream_xport_param *)ptrparam;
        !           754: 
        !           755:                        switch(xparam->op) {
        !           756:                                case STREAM_XPORT_OP_CONNECT:
        !           757:                                case STREAM_XPORT_OP_CONNECT_ASYNC:
        !           758:                                        xparam->outputs.returncode = php_tcp_sockop_connect(stream, sock, xparam TSRMLS_CC);
        !           759:                                        return PHP_STREAM_OPTION_RETURN_OK;
        !           760: 
        !           761:                                case STREAM_XPORT_OP_BIND:
        !           762:                                        xparam->outputs.returncode = php_tcp_sockop_bind(stream, sock, xparam TSRMLS_CC);
        !           763:                                        return PHP_STREAM_OPTION_RETURN_OK;
        !           764: 
        !           765: 
        !           766:                                case STREAM_XPORT_OP_ACCEPT:
        !           767:                                        xparam->outputs.returncode = php_tcp_sockop_accept(stream, sock, xparam STREAMS_CC TSRMLS_CC);
        !           768:                                        return PHP_STREAM_OPTION_RETURN_OK;
        !           769:                                default:
        !           770:                                        /* fall through */
        !           771:                                        ;
        !           772:                        }
        !           773:        }
        !           774:        return php_sockop_set_option(stream, option, value, ptrparam TSRMLS_CC);
        !           775: }
        !           776: 
        !           777: 
        !           778: PHPAPI php_stream *php_stream_generic_socket_factory(const char *proto, long protolen,
        !           779:                char *resourcename, long resourcenamelen,
        !           780:                const char *persistent_id, int options, int flags,
        !           781:                struct timeval *timeout,
        !           782:                php_stream_context *context STREAMS_DC TSRMLS_DC)
        !           783: {
        !           784:        php_stream *stream = NULL;
        !           785:        php_netstream_data_t *sock;
        !           786:        php_stream_ops *ops;
        !           787: 
        !           788:        /* which type of socket ? */
        !           789:        if (strncmp(proto, "tcp", protolen) == 0) {
        !           790:                ops = &php_stream_socket_ops;
        !           791:        } else if (strncmp(proto, "udp", protolen) == 0) {
        !           792:                ops = &php_stream_udp_socket_ops;
        !           793:        }
        !           794: #ifdef AF_UNIX
        !           795:        else if (strncmp(proto, "unix", protolen) == 0) {
        !           796:                ops = &php_stream_unix_socket_ops;
        !           797:        } else if (strncmp(proto, "udg", protolen) == 0) {
        !           798:                ops = &php_stream_unixdg_socket_ops;
        !           799:        }
        !           800: #endif
        !           801:        else {
        !           802:                /* should never happen */
        !           803:                return NULL;
        !           804:        }
        !           805:        
        !           806:        sock = pemalloc(sizeof(php_netstream_data_t), persistent_id ? 1 : 0);
        !           807:        memset(sock, 0, sizeof(php_netstream_data_t));
        !           808: 
        !           809:        sock->is_blocked = 1;
        !           810:        sock->timeout.tv_sec = FG(default_socket_timeout);
        !           811:        sock->timeout.tv_usec = 0;
        !           812: 
        !           813:        /* we don't know the socket until we have determined if we are binding or
        !           814:         * connecting */
        !           815:        sock->socket = -1;
        !           816:        
        !           817:        stream = php_stream_alloc_rel(ops, sock, persistent_id, "r+");
        !           818: 
        !           819:        if (stream == NULL)     {
        !           820:                pefree(sock, persistent_id ? 1 : 0);
        !           821:                return NULL;
        !           822:        }
        !           823: 
        !           824:        if (flags == 0) {
        !           825:                return stream;
        !           826:        }
        !           827: 
        !           828:        return stream;
        !           829: }
        !           830: 
        !           831: 
        !           832: /*
        !           833:  * Local variables:
        !           834:  * tab-width: 4
        !           835:  * c-basic-offset: 4
        !           836:  * End:
        !           837:  * vim600: noet sw=4 ts=4 fdm=marker
        !           838:  * vim<600: noet sw=4 ts=4
        !           839:  */

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