├── .github └── workflows │ └── wheels.yml ├── .gitignore ├── MANIFEST.in ├── Makefile ├── README.txt ├── VERSION ├── _sctp.c ├── _sctp.h ├── sctp.py ├── setup.py ├── test_local_cnx.py ├── test_loopback.py └── test_remote_cnx.py /.github/workflows/wheels.yml: -------------------------------------------------------------------------------- 1 | name: Build, test wheels & Publish 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build_test_wheels: 7 | name: Build & test wheels on ${{ matrix.os }} 8 | runs-on: ${{ matrix.os }} 9 | strategy: 10 | matrix: 11 | os: [ubuntu-latest] 12 | 13 | steps: 14 | - uses: actions/checkout@v3 15 | 16 | # Used to host cibuildwheel 17 | - uses: actions/setup-python@v3 18 | 19 | - name: Install cibuildwheel & build 20 | run: python -m pip install cibuildwheel==2.10.2 build 21 | 22 | - name: Build wheels & test 23 | run: python -m cibuildwheel --output-dir wheelhouse 24 | env: 25 | CIBW_BEFORE_ALL_LINUX: yum install -y lksctp-tools-devel 26 | #for now just a quick test of loopback 27 | CIBW_TEST_COMMAND: python3 {project}/test_loopback.py 28 | #skipping PyPy and musllinux builds 29 | CIBW_SKIP: "pp* *musllinux*" 30 | 31 | - name: Build sdist 32 | run: python -m build --sdist -o wheelhouse 33 | 34 | - uses: actions/upload-artifact@v3 35 | with: 36 | path: | 37 | ./wheelhouse/*.whl 38 | ./wheelhouse/*.tar.gz 39 | publish: 40 | runs-on: ubuntu-latest 41 | if: ${{ github.event_name == 'push' }} 42 | needs: 43 | - build_test_wheels 44 | steps: 45 | - name: setup python ${{ env.DEFAULT_PYTHON }} 46 | uses: actions/setup-python@v4 47 | - name: download artifact 48 | uses: actions/download-artifact@v3 49 | with: 50 | name: artifact 51 | path: dist/ 52 | - name: publish built artifacts to test.pypi.org 53 | uses: pypa/gh-action-pypi-publish@release/v1 54 | with: 55 | user: __token__ 56 | password: ${{ secrets.TEST_PYPI_API_TOKEN }} 57 | repository_url: https://test.pypi.org/legacy/ 58 | skip_existing: true 59 | - name: publish built artifacts to pypi.org 60 | if: ${{ startsWith(github.ref, 'refs/tag') }} 61 | uses: pypa/gh-action-pypi-publish@release/v1 62 | with: 63 | user: __token__ 64 | password: ${{ secrets.PYPI_API_TOKEN }} 65 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .nox/ 42 | .coverage 43 | .coverage.* 44 | .cache 45 | nosetests.xml 46 | coverage.xml 47 | *.cover 48 | .hypothesis/ 49 | .pytest_cache/ 50 | 51 | # Translations 52 | *.mo 53 | *.pot 54 | 55 | # Django stuff: 56 | *.log 57 | local_settings.py 58 | db.sqlite3 59 | 60 | # Flask stuff: 61 | instance/ 62 | .webassets-cache 63 | 64 | # Scrapy stuff: 65 | .scrapy 66 | 67 | # Sphinx documentation 68 | docs/_build/ 69 | 70 | # PyBuilder 71 | target/ 72 | 73 | # Jupyter Notebook 74 | .ipynb_checkpoints 75 | 76 | # IPython 77 | profile_default/ 78 | ipython_config.py 79 | 80 | # pyenv 81 | .python-version 82 | 83 | # celery beat schedule file 84 | celerybeat-schedule 85 | 86 | # SageMath parsed files 87 | *.sage.py 88 | 89 | # Environments 90 | .env 91 | .venv 92 | env/ 93 | venv/ 94 | ENV/ 95 | env.bak/ 96 | venv.bak/ 97 | 98 | # Spyder project settings 99 | .spyderproject 100 | .spyproject 101 | 102 | # Rope project settings 103 | .ropeproject 104 | 105 | # mkdocs documentation 106 | /site 107 | 108 | # mypy 109 | .mypy_cache/ 110 | .dmypy.json 111 | dmypy.json 112 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include *.c 2 | include *.h 3 | include TODO 4 | include test* 5 | include *.txt 6 | include VERSION 7 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # PYTHON_VERSION = 2.5 2 | CFLAGS = -Wall `python-config --cflags` -fPIC 3 | LDFLAGS = `python-config --ldflags` -fPIC -shared -lsctp 4 | 5 | # When/if your favorite SCTP kernel impl is at least draft 10 compliant 6 | # CFLAGS = $(CFLAGS) -DSCTP_DRAFT10_LEVEL 7 | 8 | CFLAGS += -DDEBUG 9 | 10 | all: _sctp.so 11 | 12 | clean: 13 | rm -f *.so *.o *.pyc 14 | rm -fr build/temp.* 15 | rm -fr build/lib.* 16 | rm -f dist/* 17 | 18 | _sctp.so: _sctp.o 19 | gcc $(LDFLAGS) -o _sctp.so _sctp.o 20 | 21 | _sctp.o: _sctp.c 22 | gcc $(CFLAGS) -c _sctp.c 23 | 24 | installdeps: 25 | sudo apt-get install libsctp-dev python-dev 26 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | PySCTP - SCTP bindings for Python 2 | --------------------------------- 3 | 4 | Elvis Pfützenreuter 5 | Instituto Nokia de Tecnologia (http://www.indt.org.br) 6 | epx __AT__ epx.com.br 7 | 8 | Philippe Langlois 9 | P1 Security (http://www.p1sec.com) 10 | phil __AT__ p1sec.com 11 | 12 | ====================================================================== 13 | INSTALL 14 | 15 | sudo python setup.py install 16 | 17 | * to see what this is going to install without actually doing it: 18 | python setup.py install --dry-run 19 | 20 | * to just build and not install: 21 | python setup.py build 22 | 23 | In case you want to install the module explicitely for Python 2 or 3, 24 | just replace _python_ by _python2_ / _python3_ in the commands above. 25 | 26 | ====================================================================== 27 | DEPENDENCIES: 28 | 29 | You can automatically install dependencies for Debian/Ubuntu: 30 | make installdeps 31 | 32 | Otherwise, necessary would be e.g. on Ubuntu: libsctp-dev and python-dev 33 | (python2-dev or python3-dev for an explicit version of Python) 34 | 35 | Support for Mac OSX is not tested, but should be doable through the SCTP Network 36 | Kernel Extension (NKE) available at: 37 | https://github.com/sctplab/SCTP_NKE_HighSierra 38 | 39 | ====================================================================== 40 | INTRODUCTION 41 | 42 | PySCTP gives access to the SCTP transport protocol from Python language. 43 | It extends the traditional socket interface, allowing 44 | SCTP sockets to be used in most situations where a TCP or UDP socket 45 | would work, while preserving the unique characteristics of the protocol. 46 | 47 | For more information about SCTP, go to http://www.sctp.org or RFC 4960. 48 | For discussion, sources, bugs, go to http://github.com/p1sec/pysctp 49 | 50 | In a nutshell, PySCTP can be used as follows: 51 | 52 | --------- 53 | 54 | import socket 55 | import sctp 56 | 57 | sk = sctp.sctpsocket_tcp(socket.AF_INET) 58 | sk.connect(("10.0.1.1", 36413)) 59 | 60 | ... most socket operations work for SCTP too ... 61 | 62 | sk.close() 63 | 64 | --------- 65 | 66 | The autotest programs (e.g. test_local_cnx.py) are actually good examples of 67 | pysctp usage. 68 | 69 | The BSD/Sockets SCTP extensions are defined by an IETF draft 70 | (draft-ietf-tsvwg-sctpsocket-10.txt) and PySCTP tries to map those 71 | extensions very closely. So, to really take the most advantage of 72 | SCTP and PySCTP, you must understand how the API works. You can 73 | find advice about it in the the draft itself (not incredibly easy 74 | to understand though), as well the 3rd edition of Unix Network 75 | Programming. 76 | 77 | 78 | ====================================================================== 79 | DESCRIPTION 80 | 81 | 1) The "sctp" module 82 | 83 | The "sctp" module is the Python side of the bindings. The docstrings 84 | of every class and method can give good advice of functions, but the 85 | highlights are: 86 | 87 | * sctpsocket is the root class for SCTP sockets, that ought not be used 88 | directly by the users. It does *not* inherit directly from Python 89 | standard socket; instead it *contains* a socket. That design was 90 | followed mostly because UDP-style sockets can be "peeled off" and 91 | return TCP-style sockets. 92 | 93 | sctpsocket delegates unknown methods to the socket. This ensures that 94 | methods like close(), bind(), read(), select() etc. will work as expected. 95 | If the real socket is really needed, it can be obtained with 96 | sctpsocket.sock(). 97 | 98 | * As said, "Normal" socket calls like open(), bind(), close() etc. 99 | can be used on SCTP sockets because they are delegated to the 100 | Python socket. 101 | 102 | * Users will normally use the sctpsocket_tcp (TCP style) and sctpsocket_udp 103 | (UDP style) classes. Some calls that are implemented in sctpsocket but 104 | do not make sense in a particular style are rendered invalid in each 105 | class (e.g. peeloff() in TCP-style sockets). 106 | 107 | 2) The "_sctp" module 108 | 109 | This is the C side of the bindings, that provides the "glue" between 110 | Python and the C API. The regular PySCTP user should not need to get 111 | into this, but power users and developers may be interested in it. 112 | 113 | The interface between Python and C is designed to be as simple as 114 | possible. In particular, no object is created in C side, just 115 | simple types (strings, integers, lists, tuples and dictionaries). 116 | 117 | The translation to/from complex objects is done entirely in Python. 118 | It avoids that _sctp depends on sctp. 119 | 120 | NOTE: it all has been tested agains lksctp-utils 1.0.1 and kernel 121 | 2.6.10, that come with Ubuntu Hoary. Some newer calls like connectx() 122 | depend of testing on a newer environment to be implemented. 123 | 124 | 125 | ====================================================================== 126 | License 127 | 128 | This module is licensed under the LGPL license. 129 | 130 | ====================================================================== 131 | Credits 132 | 133 | Elvis Pfützenreuter 134 | Philippe Langlois 135 | Casimiro Daniel NPRI - patch for new SCTP_* constants 136 | 137 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | pysctp 0.7.2 2 | -------------------------------------------------------------------------------- /_sctp.c: -------------------------------------------------------------------------------- 1 | /* SCTP bindings for Python 2 | * 3 | * _sctp.c: C-side bindings 4 | * 5 | * This library is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU Lesser General Public License as published by the 7 | * Free Software Foundation; either version 2.1 of the License, or (at your 8 | * option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, but WITHOUT 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 12 | * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 13 | * details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public License 16 | * along with this library; If not, see . 17 | * 18 | * Elvis Pfützenreuter (elvis.pfutzenreuter@{gmail.com,indt.org.br}) 19 | * Copyright (c) 2005 Instituto Nokia de Tecnologia 20 | */ 21 | 22 | #define PY_SSIZE_T_CLEAN 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include "_sctp.h" 33 | 34 | 35 | /* Python 2 and 3 initialization mess */ 36 | 37 | struct module_state { 38 | PyObject *error; 39 | }; 40 | 41 | #if PY_MAJOR_VERSION >= 3 42 | 43 | #define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) 44 | 45 | #else 46 | 47 | #define GETSTATE(m) (&_state) 48 | static struct module_state _state; 49 | 50 | #endif 51 | 52 | static PyObject * error_out(PyObject *m) { 53 | struct module_state *st = GETSTATE(m); 54 | PyErr_SetString(st->error, "something bad happened"); 55 | return NULL; 56 | } 57 | 58 | 59 | static PyObject* getconstant(PyObject* dummy, PyObject* args); 60 | static PyObject* have_sctp_multibuf(PyObject* dummy, PyObject* args); 61 | static PyObject* have_sctp_noconnect(PyObject* dummy, PyObject* args); 62 | static PyObject* have_sctp_prsctp(PyObject* dummy, PyObject* args); 63 | static PyObject* have_sctp_sat_network(PyObject* dummy, PyObject* args); 64 | static PyObject* have_sctp_setprimary(PyObject* dummy, PyObject* args); 65 | static PyObject* have_sctp_addip(PyObject* dummy, PyObject* args); 66 | static PyObject* get_mappedv4(PyObject* dummy, PyObject* args); 67 | static PyObject* set_mappedv4(PyObject* dummy, PyObject* args); 68 | static PyObject* get_nodelay(PyObject* dummy, PyObject* args); 69 | static PyObject* set_nodelay(PyObject* dummy, PyObject* args); 70 | static PyObject* get_initparams(PyObject* dummy, PyObject* args); 71 | static PyObject* set_initparams(PyObject* dummy, PyObject* args); 72 | static PyObject* peeloff(PyObject* dummy, PyObject* args); 73 | static PyObject* get_events(PyObject* dummy, PyObject* args); 74 | static PyObject* set_events(PyObject* dummy, PyObject* args); 75 | static PyObject* get_maxseg(PyObject* dummy, PyObject* args); 76 | static PyObject* set_maxseg(PyObject* dummy, PyObject* args); 77 | static PyObject* get_disable_fragments(PyObject* dummy, PyObject* args); 78 | static PyObject* set_disable_fragments(PyObject* dummy, PyObject* args); 79 | static PyObject* get_autoclose(PyObject* dummy, PyObject* args); 80 | static PyObject* set_autoclose(PyObject* dummy, PyObject* args); 81 | static PyObject* get_adaptation(PyObject* dummy, PyObject* args); 82 | static PyObject* set_adaptation(PyObject* dummy, PyObject* args); 83 | static PyObject* get_sndbuf(PyObject* dummy, PyObject* args); 84 | static PyObject* set_sndbuf(PyObject* dummy, PyObject* args); 85 | static PyObject* get_rcvbuf(PyObject* dummy, PyObject* args); 86 | static PyObject* set_rcvbuf(PyObject* dummy, PyObject* args); 87 | static PyObject* set_peer_primary(PyObject* dummy, PyObject* args); 88 | static PyObject* set_primary(PyObject* dummy, PyObject* args); 89 | static PyObject* bindx(PyObject* dummy, PyObject* args); 90 | static PyObject* connectx(PyObject* dummy, PyObject* args); 91 | static PyObject* getpaddrs(PyObject* dummy, PyObject* args); 92 | static PyObject* getladdrs(PyObject* dummy, PyObject* args); 93 | static PyObject* sctp_send_msg(PyObject* dummy, PyObject* args); 94 | static PyObject* sctp_recv_msg(PyObject* dummy, PyObject* args); 95 | static PyObject* _sockaddr_test(PyObject* dummy, PyObject* args); 96 | 97 | static PyObject* get_status(PyObject* dummy, PyObject* args); 98 | static PyObject* get_rtoinfo(PyObject* dummy, PyObject* args); 99 | static PyObject* get_paddrinfo(PyObject* dummy, PyObject* args); 100 | static PyObject* get_assocparams(PyObject* dummy, PyObject* args); 101 | static PyObject* get_paddrparams(PyObject* dummy, PyObject* args); 102 | 103 | static PyObject* set_rtoinfo(PyObject* dummy, PyObject* args); 104 | static PyObject* set_assocparams(PyObject* dummy, PyObject* args); 105 | static PyObject* set_paddrparams(PyObject* dummy, PyObject* args); 106 | 107 | static int to_sockaddr(const char *caddr, int port, struct sockaddr* saddr, int* slen); 108 | static int from_sockaddr(struct sockaddr* saddr, int* family, int* slen, int* port, char* caddr, int cnt); 109 | 110 | static PyMethodDef _sctp_methods[] = 111 | { 112 | {"error_out", (PyCFunction)error_out, METH_NOARGS, NULL}, 113 | {"getconstant", getconstant, METH_VARARGS, ""}, 114 | {"have_sctp_multibuf", have_sctp_multibuf, METH_VARARGS, ""}, 115 | {"have_sctp_noconnect", have_sctp_noconnect, METH_VARARGS, ""}, 116 | {"have_sctp_sat_network", have_sctp_sat_network, METH_VARARGS, ""}, 117 | {"have_sctp_setprimary", have_sctp_setprimary, METH_VARARGS, ""}, 118 | {"have_sctp_prsctp", have_sctp_prsctp, METH_VARARGS, ""}, 119 | {"have_sctp_addip", have_sctp_addip, METH_VARARGS, ""}, 120 | {"bindx", bindx, METH_VARARGS, ""}, 121 | {"connectx", connectx, METH_VARARGS, ""}, 122 | {"getpaddrs", getpaddrs, METH_VARARGS, ""}, 123 | {"getladdrs", getladdrs, METH_VARARGS, ""}, 124 | {"peeloff", peeloff, METH_VARARGS, ""}, 125 | {"sctp_send_msg", sctp_send_msg, METH_VARARGS, ""}, 126 | {"sctp_recv_msg", sctp_recv_msg, METH_VARARGS, ""}, 127 | {"set_peer_primary", set_peer_primary, METH_VARARGS, ""}, 128 | {"set_primary", set_primary, METH_VARARGS, ""}, 129 | {"get_autoclose", get_autoclose, METH_VARARGS, ""}, 130 | {"set_autoclose", set_autoclose, METH_VARARGS, ""}, 131 | {"get_initparams", get_initparams, METH_VARARGS, ""}, 132 | {"set_initparams", set_initparams, METH_VARARGS, ""}, 133 | {"get_nodelay", get_nodelay, METH_VARARGS, ""}, 134 | {"set_nodelay", set_nodelay, METH_VARARGS, ""}, 135 | {"get_adaptation", get_adaptation, METH_VARARGS, ""}, 136 | {"set_adaptation", set_adaptation, METH_VARARGS, ""}, 137 | {"get_sndbuf", get_sndbuf, METH_VARARGS, ""}, 138 | {"set_sndbuf", set_sndbuf, METH_VARARGS, ""}, 139 | {"get_rcvbuf", get_rcvbuf, METH_VARARGS, ""}, 140 | {"set_rcvbuf", set_rcvbuf, METH_VARARGS, ""}, 141 | {"get_disable_fragments", get_disable_fragments, METH_VARARGS, ""}, 142 | {"set_disable_fragments", set_disable_fragments, METH_VARARGS, ""}, 143 | {"get_events", get_events, METH_VARARGS, ""}, 144 | {"set_events", set_events, METH_VARARGS, ""}, 145 | {"get_mappedv4", get_mappedv4, METH_VARARGS, ""}, 146 | {"set_mappedv4", set_mappedv4, METH_VARARGS, ""}, 147 | {"get_maxseg", get_maxseg, METH_VARARGS, ""}, 148 | {"set_maxseg", set_maxseg, METH_VARARGS, ""}, 149 | {"_sockaddr_test", _sockaddr_test, METH_VARARGS, ""}, 150 | {"get_status", get_status, METH_VARARGS, ""}, 151 | {"get_rtoinfo", get_rtoinfo, METH_VARARGS, ""}, 152 | {"get_paddrinfo", get_paddrinfo, METH_VARARGS, ""}, 153 | {"get_assocparams", get_assocparams, METH_VARARGS, ""}, 154 | {"get_paddrparams", get_paddrparams, METH_VARARGS, ""}, 155 | {"set_rtoinfo", set_rtoinfo, METH_VARARGS, ""}, 156 | {"set_assocparams", set_assocparams, METH_VARARGS, ""}, 157 | {"set_paddrparams", set_paddrparams, METH_VARARGS, ""}, 158 | { NULL, NULL, 0, NULL } 159 | }; 160 | 161 | // replacing oldschool CPython module init, with more recent routines 162 | 163 | #if PY_MAJOR_VERSION >= 3 164 | 165 | #define Py23_PyLong_FromLong PyLong_FromLong 166 | #define Py23_PyLong_Check PyLong_Check 167 | #define Py23_PyLong_AsLong PyLong_AsLong 168 | #define Py23_PyUnicode_FromFormat PyUnicode_FromFormat 169 | 170 | static int _sctp_traverse(PyObject *m, visitproc visit, void *arg) { 171 | Py_VISIT(GETSTATE(m)->error); 172 | return 0; 173 | } 174 | 175 | static int _sctp_clear(PyObject *m) { 176 | Py_CLEAR(GETSTATE(m)->error); 177 | return 0; 178 | } 179 | 180 | static struct PyModuleDef moduledef = { 181 | PyModuleDef_HEAD_INIT, 182 | "_sctp", 183 | "SCTP protocol low-level bindings", 184 | sizeof(struct module_state), 185 | _sctp_methods, 186 | NULL, 187 | _sctp_traverse, 188 | _sctp_clear, 189 | NULL 190 | }; 191 | 192 | #define INITERROR return NULL 193 | 194 | PyObject * PyInit__sctp(void) 195 | 196 | #else 197 | 198 | #define INITERROR return 199 | // adding some conversion for the CPython 2 API 200 | #define Py23_PyLong_FromLong PyInt_FromLong 201 | #define Py23_PyLong_Check PyInt_Check 202 | #define Py23_PyLong_AsLong PyInt_AsLong 203 | #define Py23_PyUnicode_FromFormat PyString_FromFormat 204 | 205 | void init_sctp(void) 206 | 207 | #endif 208 | 209 | { 210 | #if PY_MAJOR_VERSION >= 3 211 | 212 | PyObject *module = PyModule_Create(&moduledef); 213 | 214 | #else 215 | 216 | PyObject *module = Py_InitModule4( 217 | "_sctp", 218 | _sctp_methods, 219 | "SCTP protocol low-level bindings", 220 | 0, 221 | PYTHON_API_VERSION); 222 | 223 | #endif 224 | 225 | if (module == NULL) 226 | INITERROR; 227 | struct module_state *st = GETSTATE(module); 228 | 229 | st->error = PyErr_NewException("_sctp.Error", NULL, NULL); 230 | if (st->error == NULL) { 231 | Py_DECREF(module); 232 | INITERROR; 233 | } 234 | 235 | #if PY_MAJOR_VERSION >= 3 236 | 237 | return module; 238 | 239 | #endif 240 | } 241 | 242 | /* 243 | 244 | static PyModuleDef sctpmodule = { 245 | PyModuleDef_HEAD_INIT, 246 | "_sctp", 247 | "SCTP protocol low-level bindings", 248 | -1, 249 | _sctpMethods, 250 | NULL, NULL, NULL, NULL 251 | }; 252 | 253 | PyMODINIT_FUNC PyInit__sctp(void) 254 | { 255 | PyObject* m; 256 | m = PyModule_Create(&sctpmodule); 257 | if(m == NULL) 258 | return NULL; 259 | return m; 260 | }; 261 | 262 | */ 263 | 264 | typedef struct ktuple { 265 | char* key; 266 | int value; 267 | } ktuple; 268 | 269 | #ifndef MSG_UNORDERED 270 | /* Newest version of SCTP Sockets changed macro names to SCTP_* to avoid MSG_* namespace pollution */ 271 | #define MSG_UNORDERED SCTP_UNORDERED 272 | #define MSG_ADDR_OVER SCTP_ADDR_OVER 273 | #define MSG_ABORT SCTP_ABORT 274 | #define MSG_EOF SCTP_EOF 275 | #endif 276 | 277 | static ktuple _constants[] = 278 | { 279 | {"BINDX_ADD", SCTP_BINDX_ADD_ADDR}, 280 | {"BINDX_REMOVE", SCTP_BINDX_REM_ADDR}, 281 | {"SOL_SCTP", SOL_SCTP}, 282 | {"IPPROTO_SCTP", IPPROTO_SCTP}, 283 | {"SOCK_SEQPACKET", SOCK_SEQPACKET}, 284 | {"SOCK_STREAM", SOCK_STREAM}, 285 | {"MSG_UNORDERED", MSG_UNORDERED}, 286 | {"MSG_ADDR_OVER", MSG_ADDR_OVER}, 287 | #ifdef SCTP_DRAFT10_LEVEL 288 | {"MSG_SENDALL", MSG_SENDALL}, 289 | #else 290 | {"MSG_SENDALL", 0}, 291 | #endif 292 | {"MSG_ABORT", MSG_ABORT}, 293 | {"MSG_EOF", MSG_EOF}, 294 | {"MSG_EOR", MSG_EOR}, 295 | {"MSG_FIN", MSG_FIN}, 296 | {"MSG_DONTROUTE", MSG_DONTROUTE}, 297 | {"MSG_NOTIFICATION", MSG_NOTIFICATION}, 298 | {"SCTP_COMM_UP", SCTP_COMM_UP}, 299 | {"SCTP_COMM_LOST", SCTP_COMM_LOST}, 300 | {"SCTP_DATA_UNSENT", SCTP_DATA_UNSENT}, 301 | {"SCTP_PARTIAL_DELIVERY_ABORTED", SCTP_PARTIAL_DELIVERY_ABORTED}, 302 | #ifdef SCTP_DRAFT10_LEVEL 303 | {"SPP_HB_DISABLED", SPP_HB_DISABLED}, 304 | {"SPP_HB_ENABLED", SPP_HB_ENABLED}, 305 | {"SPP_PMTUD_DISABLED", SPP_PMTUD_DISABLED}, 306 | {"SPP_PMTUD_ENABLED", SPP_PMTUD_ENABLED}, 307 | {"SPP_SACKDELAY_DISABLED", SPP_SACKDELAY_DISABLED}, 308 | {"SPP_SACKDELAY_ENABLED", SPP_SACKDELAY_ENABLED}, 309 | #else 310 | {"SPP_HB_DISABLED", 0}, 311 | {"SPP_HB_ENABLED", 0}, 312 | {"SPP_PMTUD_DISABLED", 0}, 313 | {"SPP_PMTUD_ENABLED", 0}, 314 | {"SPP_SACKDELAY_DISABLED", 0}, 315 | {"SPP_SACKDELAY_ENABLED", 0}, 316 | #endif 317 | #ifdef SCTP_DRAFT10_LEVEL 318 | {"SCTP_BOUND", SCTP_BOUND}, 319 | {"SCTP_LISTEN", SCTP_LISTEN}, 320 | #else 321 | {"SCTP_BOUND", -1}, 322 | {"SCTP_LISTEN", -1}, 323 | #endif 324 | {"SCTP_DATA_SENT", SCTP_DATA_SENT}, 325 | {"SCTP_RESTART", SCTP_RESTART}, 326 | {"SCTP_SHUTDOWN_COMP", SCTP_SHUTDOWN_COMP}, 327 | {"SCTP_CANT_STR_ASSOC", SCTP_CANT_STR_ASSOC}, 328 | {"SCTP_FAILED_THRESHOLD", SCTP_FAILED_THRESHOLD}, 329 | {"SCTP_RECEIVED_SACK", SCTP_RECEIVED_SACK}, 330 | {"SCTP_HEARTBEAT_SUCCESS", SCTP_HEARTBEAT_SUCCESS}, 331 | {"SCTP_RESPONSE_TO_USER_REQ", SCTP_RESPONSE_TO_USER_REQ}, 332 | {"SCTP_INTERNAL_ERROR", SCTP_INTERNAL_ERROR}, 333 | {"SCTP_SHUTDOWN_GUARD_EXPIRES", SCTP_SHUTDOWN_GUARD_EXPIRES}, 334 | {"SCTP_PEER_FAULTY", SCTP_PEER_FAULTY}, 335 | {"SCTP_ADDR_AVAILABLE", SCTP_ADDR_AVAILABLE}, 336 | {"SCTP_ADDR_UNREACHABLE", SCTP_ADDR_UNREACHABLE}, 337 | {"SCTP_ADDR_REMOVED", SCTP_ADDR_REMOVED}, 338 | {"SCTP_ADDR_MADE_PRIM", SCTP_ADDR_MADE_PRIM}, 339 | {"SCTP_ADDR_ADDED", SCTP_ADDR_ADDED}, 340 | {"SCTP_INACTIVE", SCTP_INACTIVE}, 341 | {"SCTP_ACTIVE", SCTP_ACTIVE}, 342 | {"SCTP_EMPTY", SCTP_EMPTY}, 343 | {"SCTP_CLOSED", SCTP_CLOSED}, 344 | {"SCTP_COOKIE_WAIT", SCTP_COOKIE_WAIT}, 345 | {"SCTP_COOKIE_ECHOED", SCTP_COOKIE_ECHOED}, 346 | {"SCTP_ESTABLISHED", SCTP_ESTABLISHED}, 347 | {"SCTP_SHUTDOWN_PENDING", SCTP_SHUTDOWN_PENDING}, 348 | {"SCTP_SHUTDOWN_SENT", SCTP_SHUTDOWN_SENT}, 349 | {"SCTP_SHUTDOWN_RECEIVED", SCTP_SHUTDOWN_RECEIVED}, 350 | {"SCTP_SHUTDOWN_ACK_SENT", SCTP_SHUTDOWN_ACK_SENT}, 351 | {"SCTP_SN_TYPE_BASE", SCTP_SN_TYPE_BASE}, 352 | {"SCTP_ASSOC_CHANGE", SCTP_ASSOC_CHANGE}, 353 | {"SCTP_PEER_ADDR_CHANGE", SCTP_PEER_ADDR_CHANGE}, 354 | {"SCTP_SEND_FAILED", SCTP_SEND_FAILED}, 355 | {"SCTP_REMOTE_ERROR", SCTP_REMOTE_ERROR}, 356 | {"SCTP_SHUTDOWN_EVENT", SCTP_SHUTDOWN_EVENT}, 357 | {"SCTP_PARTIAL_DELIVERY_EVENT", SCTP_PARTIAL_DELIVERY_EVENT}, 358 | {"SCTP_ADAPTATION_INDICATION", SCTP_ADAPTATION_INDICATION}, 359 | {0, -1} 360 | }; 361 | 362 | static PyObject* getconstant(PyObject* dummy, PyObject* args) 363 | { 364 | PyObject* ret = 0; 365 | 366 | char* needle; 367 | const struct ktuple* haystack; 368 | 369 | if (PyArg_ParseTuple(args, "s", &needle)) { 370 | for(haystack = &(_constants[0]); haystack->key; ++haystack) { 371 | if (strcmp(haystack->key, needle) == 0) { 372 | ret = Py23_PyLong_FromLong(haystack->value); 373 | break; 374 | } 375 | } 376 | } 377 | 378 | return ret; 379 | } 380 | 381 | static PyObject* have_sctp_sat_network(PyObject* dummy, PyObject* args) 382 | { 383 | PyObject* ret = 0; 384 | #ifdef HAVE_SCTP_SAT_NETWORK_CAPABILITY 385 | ret = Py_True; Py_INCREF(ret); 386 | #else 387 | ret = Py_False; Py_INCREF(ret); 388 | #endif 389 | return ret; 390 | } 391 | 392 | static PyObject* have_sctp_setprimary(PyObject* dummy, PyObject* args) 393 | { 394 | PyObject* ret = 0; 395 | #ifdef HAVE_SCTP_CANSET_PRIMARY 396 | ret = Py_True; Py_INCREF(ret); 397 | #else 398 | ret = Py_False; Py_INCREF(ret); 399 | #endif 400 | return ret; 401 | } 402 | 403 | static PyObject* have_sctp_addip(PyObject* dummy, PyObject* args) 404 | { 405 | PyObject* ret = 0; 406 | #ifdef HAVE_SCTP_ADDIP 407 | ret = Py_True; Py_INCREF(ret); 408 | #else 409 | ret = Py_False; Py_INCREF(ret); 410 | #endif 411 | return ret; 412 | } 413 | 414 | static PyObject* have_sctp_prsctp(PyObject* dummy, PyObject* args) 415 | { 416 | PyObject* ret = 0; 417 | #ifdef HAVE_SCTP_PRSCTP 418 | ret = Py_True; Py_INCREF(ret); 419 | #else 420 | ret = Py_False; Py_INCREF(ret); 421 | #endif 422 | return ret; 423 | } 424 | 425 | static PyObject* have_sctp_multibuf(PyObject* dummy, PyObject* args) 426 | { 427 | PyObject* ret = 0; 428 | #ifdef HAVE_SCTP_MULTIBUF 429 | ret = Py_True; Py_INCREF(ret); 430 | #else 431 | ret = Py_False; Py_INCREF(ret); 432 | #endif 433 | return ret; 434 | } 435 | 436 | static PyObject* have_sctp_noconnect(PyObject* dummy, PyObject* args) 437 | { 438 | PyObject* ret = 0; 439 | #ifdef HAVE_SCTP_NOCONNECT 440 | ret = Py_True; Py_INCREF(ret); 441 | #else 442 | ret = Py_False; Py_INCREF(ret); 443 | #endif 444 | return ret; 445 | } 446 | 447 | static PyObject* get_mappedv4(PyObject* dummy, PyObject* args) 448 | { 449 | PyObject* ret = 0; 450 | int fd, v; 451 | socklen_t lv = sizeof(v); 452 | if (PyArg_ParseTuple(args, "i", &fd)) { 453 | if (getsockopt(fd, SOL_SCTP, SCTP_I_WANT_MAPPED_V4_ADDR, &v, &lv)) { 454 | PyErr_SetFromErrno(PyExc_IOError); 455 | } else { 456 | ret = PyBool_FromLong(v); 457 | } 458 | } 459 | return ret; 460 | } 461 | 462 | static PyObject* set_mappedv4(PyObject* dummy, PyObject* args) 463 | { 464 | PyObject* ret = 0; 465 | int fd, v; 466 | if (PyArg_ParseTuple(args, "ii", &fd, &v)) { 467 | if (setsockopt(fd, SOL_SCTP, SCTP_I_WANT_MAPPED_V4_ADDR, &v, sizeof(v))) { 468 | PyErr_SetFromErrno(PyExc_IOError); 469 | } else { 470 | ret = Py_None; Py_INCREF(ret); 471 | } 472 | } 473 | return ret; 474 | } 475 | 476 | static PyObject* get_nodelay(PyObject* dummy, PyObject* args) 477 | { 478 | PyObject* ret = 0; 479 | int fd, v; 480 | socklen_t lv = sizeof(v); 481 | if (PyArg_ParseTuple(args, "i", &fd)) { 482 | if (getsockopt(fd, SOL_SCTP, SCTP_NODELAY, &v, &lv)) { 483 | PyErr_SetFromErrno(PyExc_IOError); 484 | } else { 485 | ret = PyBool_FromLong(v); 486 | } 487 | } 488 | return ret; 489 | } 490 | 491 | static PyObject* set_nodelay(PyObject* dummy, PyObject* args) 492 | { 493 | PyObject* ret = 0; 494 | int fd, v; 495 | if (PyArg_ParseTuple(args, "ii", &fd, &v)) { 496 | if (setsockopt(fd, SOL_SCTP, SCTP_NODELAY, &v, sizeof(v))) { 497 | PyErr_SetFromErrno(PyExc_IOError); 498 | } else { 499 | ret = Py_None; Py_INCREF(ret); 500 | } 501 | } 502 | return ret; 503 | } 504 | 505 | static PyObject* get_assocparams(PyObject* dummy, PyObject* args) 506 | { 507 | PyObject* ret = 0; 508 | PyObject* dict; 509 | PyObject* oassoc_id; 510 | int fd; 511 | struct sctp_assocparams v; 512 | socklen_t lv = sizeof(v); 513 | int ok; 514 | 515 | ok = PyArg_ParseTuple(args, "iO", &fd, &dict) && PyDict_Check(dict); 516 | ok = ok && (oassoc_id = PyDict_GetItemString(dict, "assoc_id")); 517 | ok = ok && Py23_PyLong_Check(oassoc_id); 518 | 519 | if (! ok) { 520 | return ret; 521 | } 522 | 523 | bzero(&v, sizeof(v)); 524 | v.sasoc_assoc_id = Py23_PyLong_AsLong(oassoc_id); 525 | 526 | if (getsockopt(fd, SOL_SCTP, SCTP_ASSOCINFO, &v, &lv)) { 527 | PyErr_SetFromErrno(PyExc_IOError); 528 | } else { 529 | PyDict_SetItemString(dict, "assocmaxrxt", Py23_PyLong_FromLong(v.sasoc_asocmaxrxt)); 530 | PyDict_SetItemString(dict, "number_peer_destinations", Py23_PyLong_FromLong(v.sasoc_number_peer_destinations)); 531 | PyDict_SetItemString(dict, "peer_rwnd", Py23_PyLong_FromLong(v.sasoc_peer_rwnd)); 532 | PyDict_SetItemString(dict, "local_rwnd", Py23_PyLong_FromLong(v.sasoc_local_rwnd)); 533 | PyDict_SetItemString(dict, "cookie_life", Py23_PyLong_FromLong(v.sasoc_cookie_life)); 534 | ret = Py_None; Py_INCREF(ret); 535 | } 536 | 537 | return ret; 538 | } 539 | 540 | static PyObject* set_assocparams(PyObject* dummy, PyObject* args) 541 | { 542 | PyObject* ret = 0; 543 | PyObject* dict; 544 | PyObject* oassoc_id; 545 | PyObject* oassocmaxrxt; 546 | PyObject* onumber_peer_destinations; 547 | PyObject* opeer_rwnd; 548 | PyObject* olocal_rwnd; 549 | PyObject* ocookie_life; 550 | 551 | int fd; 552 | struct sctp_assocparams v; 553 | int ok; 554 | 555 | ok = PyArg_ParseTuple(args, "iO", &fd, &dict) && PyDict_Check(dict); 556 | ok = ok && (oassoc_id = PyDict_GetItemString(dict, "assoc_id")); 557 | ok = ok && (oassocmaxrxt = PyDict_GetItemString(dict, "assocmaxrxt")); 558 | ok = ok && (onumber_peer_destinations = PyDict_GetItemString(dict, "number_peer_destinations")); 559 | ok = ok && (opeer_rwnd = PyDict_GetItemString(dict, "peer_rwnd")); 560 | ok = ok && (olocal_rwnd = PyDict_GetItemString(dict, "local_rwnd")); 561 | ok = ok && (ocookie_life = PyDict_GetItemString(dict, "cookie_life")); 562 | ok = ok && Py23_PyLong_Check(oassoc_id); 563 | ok = ok && Py23_PyLong_Check(oassocmaxrxt); 564 | ok = ok && Py23_PyLong_Check(onumber_peer_destinations); 565 | ok = ok && Py23_PyLong_Check(opeer_rwnd); 566 | ok = ok && Py23_PyLong_Check(olocal_rwnd); 567 | ok = ok && Py23_PyLong_Check(ocookie_life); 568 | 569 | if (! ok) { 570 | return ret; 571 | } 572 | 573 | bzero(&v, sizeof(v)); 574 | v.sasoc_assoc_id = Py23_PyLong_AsLong(oassoc_id); 575 | v.sasoc_asocmaxrxt = Py23_PyLong_AsLong(oassocmaxrxt); 576 | v.sasoc_number_peer_destinations = Py23_PyLong_AsLong(onumber_peer_destinations); 577 | v.sasoc_peer_rwnd = Py23_PyLong_AsLong(opeer_rwnd); 578 | v.sasoc_local_rwnd = Py23_PyLong_AsLong(olocal_rwnd); 579 | v.sasoc_cookie_life = Py23_PyLong_AsLong(ocookie_life); 580 | 581 | if (setsockopt(fd, SOL_SCTP, SCTP_ASSOCINFO, &v, sizeof(v))) { 582 | PyErr_SetFromErrno(PyExc_IOError); 583 | } else { 584 | PyDict_SetItemString(dict, "assocmaxrxt", Py23_PyLong_FromLong(v.sasoc_asocmaxrxt)); 585 | PyDict_SetItemString(dict, "number_peer_destinations", Py23_PyLong_FromLong(v.sasoc_number_peer_destinations)); 586 | PyDict_SetItemString(dict, "peer_rwnd", Py23_PyLong_FromLong(v.sasoc_peer_rwnd)); 587 | PyDict_SetItemString(dict, "local_rwnd", Py23_PyLong_FromLong(v.sasoc_local_rwnd)); 588 | PyDict_SetItemString(dict, "cookie_life", Py23_PyLong_FromLong(v.sasoc_cookie_life)); 589 | ret = Py_None; Py_INCREF(ret); 590 | } 591 | 592 | return ret; 593 | } 594 | 595 | static PyObject* get_paddrparams(PyObject* dummy, PyObject* args) 596 | { 597 | PyObject* ret = 0; 598 | PyObject* dict; 599 | PyObject* oassoc_id; 600 | PyObject *oaddresstuple; 601 | 602 | const char* address; 603 | int port; 604 | int fd; 605 | int slen_dummy; 606 | 607 | struct sctp_paddrparams v; 608 | socklen_t lv = sizeof(v); 609 | int ok; 610 | 611 | ok = PyArg_ParseTuple(args, "iO", &fd, &dict) && PyDict_Check(dict); 612 | ok = ok && (oassoc_id = PyDict_GetItemString(dict, "assoc_id")); 613 | ok = ok && (oaddresstuple = PyDict_GetItemString(dict, "sockaddr")); 614 | ok = ok && PyArg_ParseTuple(oaddresstuple, "si", &address, &port); 615 | ok = ok && Py23_PyLong_Check(oassoc_id); 616 | 617 | if (! ok) { 618 | return ret; 619 | } 620 | 621 | bzero(&v, sizeof(v)); 622 | v.spp_assoc_id = Py23_PyLong_AsLong(oassoc_id); 623 | 624 | if (! to_sockaddr(address, port, (struct sockaddr*) &(v.spp_address), &slen_dummy)) { 625 | PyErr_SetString(PyExc_ValueError, "address could not be translated"); 626 | return ret; 627 | } 628 | 629 | if (getsockopt(fd, SOL_SCTP, SCTP_PEER_ADDR_PARAMS, &v, &lv)) { 630 | PyErr_SetFromErrno(PyExc_IOError); 631 | } else { 632 | PyDict_SetItemString(dict, "hbinterval", Py23_PyLong_FromLong(v.spp_hbinterval)); 633 | PyDict_SetItemString(dict, "pathmaxrxt", Py23_PyLong_FromLong(v.spp_pathmaxrxt)); 634 | #ifdef SCTP_DRAFT10_LEVEL 635 | PyDict_SetItemString(dict, "pathmtu", Py23_PyLong_FromLong(v.spp_pathmtu)); 636 | PyDict_SetItemString(dict, "sackdelay", Py23_PyLong_FromLong(v.spp_sackdelay)); 637 | PyDict_SetItemString(dict, "flags", Py23_PyLong_FromLong(v.spp_flags)); 638 | #endif 639 | ret = Py_None; Py_INCREF(ret); 640 | } 641 | 642 | return ret; 643 | } 644 | 645 | static PyObject* set_paddrparams(PyObject* dummy, PyObject* args) 646 | { 647 | PyObject* ret = 0; 648 | PyObject* dict; 649 | PyObject* oassoc_id; 650 | PyObject *oaddresstuple; 651 | PyObject* ohbinterval; 652 | PyObject* opathmaxrxt; 653 | PyObject* opathmtu; 654 | PyObject* osackdelay; 655 | PyObject* oflags; 656 | 657 | const char* address; 658 | int port; 659 | 660 | int fd; 661 | struct sctp_paddrparams v; 662 | int ok; 663 | int slen_dummy; 664 | 665 | ok = PyArg_ParseTuple(args, "iO", &fd, &dict) && PyDict_Check(dict); 666 | 667 | ok = ok && (oassoc_id = PyDict_GetItemString(dict, "assoc_id")); 668 | ok = ok && (oaddresstuple = PyDict_GetItemString(dict, "sockaddr")); 669 | ok = ok && (ohbinterval = PyDict_GetItemString(dict, "hbinterval")); 670 | ok = ok && (opathmaxrxt = PyDict_GetItemString(dict, "pathmaxrxt")); 671 | ok = ok && (opathmtu = PyDict_GetItemString(dict, "pathmtu")); 672 | ok = ok && (osackdelay = PyDict_GetItemString(dict, "sackdelay")); 673 | ok = ok && (oflags = PyDict_GetItemString(dict, "flags")); 674 | 675 | ok = ok && PyArg_ParseTuple(oaddresstuple, "si", &address, &port); 676 | 677 | ok = ok && Py23_PyLong_Check(oassoc_id); 678 | ok = ok && Py23_PyLong_Check(ohbinterval); 679 | ok = ok && Py23_PyLong_Check(opathmaxrxt); 680 | ok = ok && Py23_PyLong_Check(opathmtu); 681 | ok = ok && Py23_PyLong_Check(osackdelay); 682 | ok = ok && Py23_PyLong_Check(oflags); 683 | 684 | if (! ok) { 685 | return ret; 686 | } 687 | 688 | bzero(&v, sizeof(v)); 689 | v.spp_assoc_id = Py23_PyLong_AsLong(oassoc_id); 690 | v.spp_hbinterval = Py23_PyLong_AsLong(ohbinterval); 691 | v.spp_pathmaxrxt = Py23_PyLong_AsLong(opathmaxrxt); 692 | #ifdef SCTP_DRAFT10_LEVEL 693 | v.spp_pathmtu = Py23_PyLong_AsLong(opathmtu); 694 | v.spp_sackdelay = Py23_PyLong_AsLong(osackdelay); 695 | v.spp_flags = Py23_PyLong_AsLong(oflags); 696 | #endif 697 | 698 | if (! to_sockaddr(address, port, (struct sockaddr*) &(v.spp_address), &slen_dummy)) { 699 | PyErr_SetString(PyExc_ValueError, "address could not be translated"); 700 | return ret; 701 | } 702 | 703 | if (setsockopt(fd, SOL_SCTP, SCTP_PEER_ADDR_PARAMS, &v, sizeof(v))) { 704 | PyErr_SetFromErrno(PyExc_IOError); 705 | } else { 706 | PyDict_SetItemString(dict, "hbinterval", Py23_PyLong_FromLong(v.spp_hbinterval)); 707 | PyDict_SetItemString(dict, "pathmaxrxt", Py23_PyLong_FromLong(v.spp_pathmaxrxt)); 708 | #ifdef SCTP_DRAFT10_LEVEL 709 | PyDict_SetItemString(dict, "pathmtu", Py23_PyLong_FromLong(v.spp_pathmtu)); 710 | PyDict_SetItemString(dict, "sackdelay", Py23_PyLong_FromLong(v.spp_sackdelay)); 711 | PyDict_SetItemString(dict, "flags", Py23_PyLong_FromLong(v.spp_flags)); 712 | #endif 713 | ret = Py_None; Py_INCREF(ret); 714 | } 715 | 716 | return ret; 717 | } 718 | 719 | static PyObject* get_status(PyObject* dummy, PyObject* args) 720 | { 721 | PyObject* ret = 0; 722 | PyObject* dict; 723 | PyObject* dict2; 724 | PyObject* oassoc_id; 725 | 726 | PyObject* oaddr; 727 | char caddr[256]; 728 | int family, len, port; 729 | 730 | int fd; 731 | struct sctp_status v; 732 | socklen_t lv = sizeof(v); 733 | int ok; 734 | 735 | ok = PyArg_ParseTuple(args, "iOO", &fd, &dict, &dict2) && \ 736 | PyDict_Check(dict) && PyDict_Check(dict2); 737 | ok = ok && (oassoc_id = PyDict_GetItemString(dict, "assoc_id")); 738 | ok = ok && Py23_PyLong_Check(oassoc_id); 739 | 740 | if (! ok) { 741 | return ret; 742 | } 743 | 744 | bzero(&v, sizeof(v)); 745 | v.sstat_assoc_id = Py23_PyLong_AsLong(oassoc_id); 746 | 747 | if (getsockopt(fd, SOL_SCTP, SCTP_STATUS, &v, &lv)) { 748 | PyErr_SetFromErrno(PyExc_IOError); 749 | } else { 750 | PyDict_SetItemString(dict, "state", Py23_PyLong_FromLong(v.sstat_state)); 751 | PyDict_SetItemString(dict, "rwnd", Py23_PyLong_FromLong(v.sstat_rwnd)); 752 | PyDict_SetItemString(dict, "unackdata", Py23_PyLong_FromLong(v.sstat_unackdata)); 753 | PyDict_SetItemString(dict, "penddata", Py23_PyLong_FromLong(v.sstat_penddata)); 754 | PyDict_SetItemString(dict, "instrms", Py23_PyLong_FromLong(v.sstat_instrms)); 755 | PyDict_SetItemString(dict, "outstrms", Py23_PyLong_FromLong(v.sstat_outstrms)); 756 | PyDict_SetItemString(dict, "fragmentation_point", Py23_PyLong_FromLong(v.sstat_fragmentation_point)); 757 | 758 | if (from_sockaddr((struct sockaddr*) &(v.sstat_primary.spinfo_address), &family, 759 | &len, &port, caddr, sizeof(caddr))) { 760 | oaddr = PyTuple_New(2); 761 | PyTuple_SetItem(oaddr, 0, PyUnicode_FromString(caddr)); 762 | PyTuple_SetItem(oaddr, 1, Py23_PyLong_FromLong(port)); 763 | } else { 764 | // something went wrong 765 | oaddr = Py_None; 766 | Py_INCREF(Py_None); 767 | } 768 | 769 | PyDict_SetItemString(dict2, "sockaddr", oaddr); 770 | PyDict_SetItemString(dict2, "assoc_id", Py23_PyLong_FromLong(v.sstat_primary.spinfo_assoc_id)); 771 | PyDict_SetItemString(dict2, "state", Py23_PyLong_FromLong(v.sstat_primary.spinfo_state)); 772 | PyDict_SetItemString(dict2, "cwnd", Py23_PyLong_FromLong(v.sstat_primary.spinfo_cwnd)); 773 | PyDict_SetItemString(dict2, "srtt", Py23_PyLong_FromLong(v.sstat_primary.spinfo_srtt)); 774 | PyDict_SetItemString(dict2, "rto", Py23_PyLong_FromLong(v.sstat_primary.spinfo_rto)); 775 | PyDict_SetItemString(dict2, "mtu", Py23_PyLong_FromLong(v.sstat_primary.spinfo_mtu)); 776 | ret = Py_None; Py_INCREF(ret); 777 | } 778 | 779 | return ret; 780 | } 781 | 782 | static PyObject* get_paddrinfo(PyObject* dummy, PyObject* args) 783 | { 784 | PyObject* ret = 0; 785 | PyObject* dict; 786 | PyObject* oassoc_id; 787 | PyObject* oaddresstuple; 788 | const char* address; 789 | int port; 790 | int fd; 791 | int slen_dummy; 792 | struct sctp_paddrinfo v; 793 | socklen_t lv = sizeof(v); 794 | int ok; 795 | 796 | ok = PyArg_ParseTuple(args, "iO", &fd, &dict) && PyDict_Check(dict); 797 | ok = ok && (oassoc_id = PyDict_GetItemString(dict, "assoc_id")); 798 | ok = ok && (oaddresstuple = PyDict_GetItemString(dict, "sockaddr")); 799 | ok = ok && Py23_PyLong_Check(oassoc_id); 800 | ok = ok && PyArg_ParseTuple(oaddresstuple, "si", &address, &port); 801 | 802 | if (! ok) { 803 | return ret; 804 | } 805 | 806 | bzero(&v, sizeof(v)); 807 | v.spinfo_assoc_id = Py23_PyLong_AsLong(oassoc_id); 808 | if (! to_sockaddr(address, port, (struct sockaddr*) &(v.spinfo_address), &slen_dummy)) { 809 | PyErr_SetString(PyExc_ValueError, "address could not be translated"); 810 | return ret; 811 | } 812 | 813 | if (getsockopt(fd, SOL_SCTP, SCTP_GET_PEER_ADDR_INFO, &v, &lv)) { 814 | PyErr_SetFromErrno(PyExc_IOError); 815 | } else { 816 | PyDict_SetItemString(dict, "state", Py23_PyLong_FromLong(v.spinfo_state)); 817 | PyDict_SetItemString(dict, "cwnd", Py23_PyLong_FromLong(v.spinfo_cwnd)); 818 | PyDict_SetItemString(dict, "srtt", Py23_PyLong_FromLong(v.spinfo_srtt)); 819 | PyDict_SetItemString(dict, "rto", Py23_PyLong_FromLong(v.spinfo_rto)); 820 | PyDict_SetItemString(dict, "mtu", Py23_PyLong_FromLong(v.spinfo_mtu)); 821 | ret = Py_None; Py_INCREF(ret); 822 | } 823 | 824 | return ret; 825 | } 826 | 827 | static PyObject* get_rtoinfo(PyObject* dummy, PyObject* args) 828 | { 829 | PyObject* ret = 0; 830 | PyObject* dict; 831 | PyObject* oassoc_id; 832 | int fd; 833 | struct sctp_rtoinfo v; 834 | socklen_t lv = sizeof(v); 835 | int ok; 836 | 837 | ok = PyArg_ParseTuple(args, "iO", &fd, &dict) && PyDict_Check(dict); 838 | ok = ok && (oassoc_id = PyDict_GetItemString(dict, "assoc_id")); 839 | ok = ok && Py23_PyLong_Check(oassoc_id); 840 | 841 | if (! ok) { 842 | return ret; 843 | } 844 | 845 | bzero(&v, sizeof(v)); 846 | v.srto_assoc_id = Py23_PyLong_AsLong(oassoc_id); 847 | 848 | if (getsockopt(fd, SOL_SCTP, SCTP_RTOINFO, &v, &lv)) { 849 | PyErr_SetFromErrno(PyExc_IOError); 850 | } else { 851 | PyDict_SetItemString(dict, "initial", Py23_PyLong_FromLong(v.srto_initial)); 852 | PyDict_SetItemString(dict, "max", Py23_PyLong_FromLong(v.srto_max)); 853 | PyDict_SetItemString(dict, "min", Py23_PyLong_FromLong(v.srto_min)); 854 | ret = Py_None; Py_INCREF(ret); 855 | } 856 | 857 | return ret; 858 | } 859 | 860 | static PyObject* set_rtoinfo(PyObject* dummy, PyObject* args) 861 | { 862 | PyObject* ret = 0; 863 | PyObject* dict; 864 | PyObject* oassoc_id; 865 | PyObject* oinitial; 866 | PyObject* omin; 867 | PyObject* omax; 868 | 869 | int fd; 870 | struct sctp_rtoinfo v; 871 | int ok; 872 | 873 | ok = PyArg_ParseTuple(args, "iO", &fd, &dict) && PyDict_Check(dict); 874 | ok = ok && (oassoc_id = PyDict_GetItemString(dict, "assoc_id")); 875 | ok = ok && (oinitial = PyDict_GetItemString(dict, "initial")); 876 | ok = ok && (omin = PyDict_GetItemString(dict, "min")); 877 | ok = ok && (omax = PyDict_GetItemString(dict, "max")); 878 | ok = ok && Py23_PyLong_Check(oassoc_id); 879 | ok = ok && Py23_PyLong_Check(oinitial); 880 | ok = ok && Py23_PyLong_Check(omin); 881 | ok = ok && Py23_PyLong_Check(omax); 882 | 883 | if (! ok) { 884 | return ret; 885 | } 886 | 887 | bzero(&v, sizeof(v)); 888 | v.srto_assoc_id = Py23_PyLong_AsLong(oassoc_id); 889 | v.srto_initial = Py23_PyLong_AsLong(oinitial); 890 | v.srto_min = Py23_PyLong_AsLong(omin); 891 | v.srto_max = Py23_PyLong_AsLong(omax); 892 | 893 | if (setsockopt(fd, SOL_SCTP, SCTP_RTOINFO, &v, sizeof(v))) { 894 | PyErr_SetFromErrno(PyExc_IOError); 895 | } else { 896 | PyDict_SetItemString(dict, "initial", Py23_PyLong_FromLong(v.srto_initial)); 897 | PyDict_SetItemString(dict, "max", Py23_PyLong_FromLong(v.srto_max)); 898 | PyDict_SetItemString(dict, "min", Py23_PyLong_FromLong(v.srto_min)); 899 | ret = Py_None; Py_INCREF(ret); 900 | } 901 | 902 | return ret; 903 | } 904 | 905 | static PyObject* get_initparams(PyObject* dummy, PyObject* args) 906 | { 907 | PyObject* ret = 0; 908 | int fd; 909 | struct sctp_initmsg v; 910 | socklen_t lv = sizeof(v); 911 | 912 | if (! PyArg_ParseTuple(args, "i", &fd)) { 913 | return ret; 914 | } 915 | 916 | if (getsockopt(fd, SOL_SCTP, SCTP_INITMSG, &v, &lv)) { 917 | PyErr_SetFromErrno(PyExc_IOError); 918 | } else { 919 | ret = PyDict_New(); 920 | PyDict_SetItemString(ret, "_num_ostreams", Py23_PyLong_FromLong(v.sinit_num_ostreams)); 921 | PyDict_SetItemString(ret, "_max_instreams", Py23_PyLong_FromLong(v.sinit_max_instreams)); 922 | PyDict_SetItemString(ret, "_max_attempts", Py23_PyLong_FromLong(v.sinit_max_attempts)); 923 | PyDict_SetItemString(ret, "_max_init_timeo", Py23_PyLong_FromLong(v.sinit_max_attempts)); 924 | } 925 | 926 | return ret; 927 | } 928 | 929 | static PyObject* set_initparams(PyObject* dummy, PyObject* args) 930 | { 931 | PyObject* ret = 0; 932 | int fd; 933 | PyObject *ov, *o_num_ostreams, *o_max_instreams, *o_max_attempts, *o_max_init_timeo; 934 | struct sctp_initmsg v; 935 | 936 | int ok = PyArg_ParseTuple(args, "iO", &fd, &ov) && PyDict_Check(ov); 937 | 938 | ok = ok && (o_num_ostreams = PyDict_GetItemString(ov, "_num_ostreams")); 939 | ok = ok && (o_max_instreams = PyDict_GetItemString(ov, "_max_instreams")); 940 | ok = ok && (o_max_attempts = PyDict_GetItemString(ov, "_max_attempts")); 941 | ok = ok && (o_max_init_timeo = PyDict_GetItemString(ov, "_max_init_timeo")); 942 | 943 | ok = ok && (Py23_PyLong_Check(o_num_ostreams) != 0); 944 | ok = ok && (Py23_PyLong_Check(o_max_instreams) != 0); 945 | ok = ok && (Py23_PyLong_Check(o_max_attempts) != 0); 946 | ok = ok && (Py23_PyLong_Check(o_max_init_timeo) != 0); 947 | 948 | if (ok) { 949 | memset(&v, 0, sizeof(v)); 950 | v.sinit_num_ostreams = Py23_PyLong_AsLong(o_num_ostreams); 951 | v.sinit_max_instreams = Py23_PyLong_AsLong(o_max_instreams); 952 | v.sinit_max_attempts = Py23_PyLong_AsLong(o_max_attempts); 953 | v.sinit_max_init_timeo = Py23_PyLong_AsLong(o_max_init_timeo); 954 | 955 | if (setsockopt(fd, SOL_SCTP, SCTP_INITMSG, &v, sizeof(v))) { 956 | PyErr_SetFromErrno(PyExc_IOError); 957 | } else { 958 | ret = Py_None; Py_INCREF(ret); 959 | } 960 | } 961 | return ret; 962 | } 963 | 964 | static PyObject* peeloff(PyObject* dummy, PyObject* args) 965 | { 966 | PyObject* ret = 0; 967 | int v1, v2, fd; 968 | 969 | if (PyArg_ParseTuple(args, "ii", &v1, &v2)) { 970 | fd = sctp_peeloff(v1, v2); 971 | if (fd < 0) { 972 | PyErr_SetFromErrno(PyExc_IOError); 973 | } else { 974 | ret = Py23_PyLong_FromLong(fd); 975 | } 976 | } 977 | 978 | return ret; 979 | } 980 | 981 | static PyObject* get_events(PyObject* dummy, PyObject* args) 982 | { 983 | PyObject* ret = 0; 984 | int fd; 985 | struct sctp_event_subscribe v; 986 | socklen_t lv = sizeof(v); 987 | 988 | if (PyArg_ParseTuple(args, "i", &fd)) { 989 | if (getsockopt(fd, SOL_SCTP, SCTP_EVENTS, &v, &lv)) { 990 | PyErr_SetFromErrno(PyExc_IOError); 991 | } else { 992 | ret = PyDict_New(); 993 | PyDict_SetItemString(ret, "_data_io", PyBool_FromLong(v.sctp_data_io_event)); 994 | PyDict_SetItemString(ret, "_association", PyBool_FromLong(v.sctp_association_event)); 995 | PyDict_SetItemString(ret, "_address", PyBool_FromLong(v.sctp_address_event)); 996 | PyDict_SetItemString(ret, "_send_failure", PyBool_FromLong(v.sctp_send_failure_event)); 997 | PyDict_SetItemString(ret, "_peer_error", PyBool_FromLong(v.sctp_peer_error_event)); 998 | PyDict_SetItemString(ret, "_shutdown", PyBool_FromLong(v.sctp_shutdown_event)); 999 | PyDict_SetItemString(ret, "_partial_delivery", PyBool_FromLong(v.sctp_partial_delivery_event)); 1000 | PyDict_SetItemString(ret, "_adaptation_layer", PyBool_FromLong(v.sctp_adaptation_layer_event)); 1001 | } 1002 | } 1003 | return ret; 1004 | } 1005 | 1006 | static PyObject* set_events(PyObject* dummy, PyObject* args) 1007 | { 1008 | PyObject* ret = 0; 1009 | int fd; 1010 | PyObject *ov, *o_data_io, *o_association, *o_address, *o_send_failure; 1011 | PyObject *o_peer_error, *o_shutdown, *o_partial_delivery, *o_adaptation_layer; 1012 | struct sctp_event_subscribe v; 1013 | int ok = PyArg_ParseTuple(args, "iO", &fd, &ov) && PyDict_Check(ov); 1014 | 1015 | ok = ok && (o_data_io = PyDict_GetItemString(ov, "_data_io")); 1016 | ok = ok && (o_association = PyDict_GetItemString(ov, "_association")); 1017 | ok = ok && (o_address = PyDict_GetItemString(ov, "_address")); 1018 | ok = ok && (o_send_failure = PyDict_GetItemString(ov, "_send_failure")); 1019 | ok = ok && (o_peer_error = PyDict_GetItemString(ov, "_peer_error")); 1020 | ok = ok && (o_shutdown = PyDict_GetItemString(ov, "_shutdown")); 1021 | ok = ok && (o_partial_delivery = PyDict_GetItemString(ov, "_partial_delivery")); 1022 | ok = ok && (o_adaptation_layer = PyDict_GetItemString(ov, "_adaptation_layer")); 1023 | 1024 | ok = ok && (Py23_PyLong_Check(o_data_io) != 0); 1025 | ok = ok && (Py23_PyLong_Check(o_association) != 0); 1026 | ok = ok && (Py23_PyLong_Check(o_address) != 0); 1027 | ok = ok && (Py23_PyLong_Check(o_send_failure) != 0); 1028 | ok = ok && (Py23_PyLong_Check(o_peer_error) != 0); 1029 | ok = ok && (Py23_PyLong_Check(o_shutdown) != 0); 1030 | ok = ok && (Py23_PyLong_Check(o_send_failure) != 0); 1031 | ok = ok && (Py23_PyLong_Check(o_adaptation_layer) != 0); 1032 | 1033 | if (ok) { 1034 | memset(&v, 0, sizeof(v)); 1035 | v.sctp_data_io_event = Py23_PyLong_AsLong(o_data_io); 1036 | v.sctp_association_event = Py23_PyLong_AsLong(o_association); 1037 | v.sctp_address_event = Py23_PyLong_AsLong(o_address); 1038 | v.sctp_send_failure_event = Py23_PyLong_AsLong(o_send_failure); 1039 | v.sctp_peer_error_event = Py23_PyLong_AsLong(o_peer_error); 1040 | v.sctp_shutdown_event = Py23_PyLong_AsLong(o_shutdown); 1041 | v.sctp_partial_delivery_event = Py23_PyLong_AsLong(o_partial_delivery); 1042 | v.sctp_adaptation_layer_event = Py23_PyLong_AsLong(o_adaptation_layer); 1043 | 1044 | if (setsockopt(fd, SOL_SCTP, SCTP_EVENTS, &v, sizeof(v))) { 1045 | PyErr_SetFromErrno(PyExc_IOError); 1046 | } else { 1047 | ret = Py_None; Py_INCREF(ret); 1048 | } 1049 | } 1050 | return ret; 1051 | } 1052 | 1053 | static PyObject* get_maxseg(PyObject* dummy, PyObject* args) 1054 | { 1055 | PyObject* ret = 0; 1056 | int fd, v; 1057 | socklen_t lv = sizeof(v); 1058 | 1059 | if (PyArg_ParseTuple(args, "i", &fd)) { 1060 | if (getsockopt(fd, SOL_SCTP, SCTP_MAXSEG, &v, &lv)) { 1061 | PyErr_SetFromErrno(PyExc_IOError); 1062 | } else { 1063 | ret = Py23_PyLong_FromLong(v); 1064 | } 1065 | } 1066 | return ret; 1067 | } 1068 | 1069 | static PyObject* set_maxseg(PyObject* dummy, PyObject* args) 1070 | { 1071 | PyObject* ret = 0; 1072 | int fd, v; 1073 | 1074 | if (PyArg_ParseTuple(args, "ii", &fd, &v)) { 1075 | if (setsockopt(fd, SOL_SCTP, SCTP_MAXSEG, &v, sizeof(v))) { 1076 | PyErr_SetFromErrno(PyExc_IOError); 1077 | } else { 1078 | ret = Py_None; Py_INCREF(ret); 1079 | } 1080 | } 1081 | return ret; 1082 | } 1083 | 1084 | static PyObject* get_disable_fragments(PyObject* dummy, PyObject* args) 1085 | { 1086 | PyObject* ret = 0; 1087 | int fd, v; 1088 | socklen_t lv = sizeof(v); 1089 | 1090 | if (PyArg_ParseTuple(args, "i", &fd)) { 1091 | if (getsockopt(fd, SOL_SCTP, SCTP_DISABLE_FRAGMENTS, &v, &lv)) { 1092 | PyErr_SetFromErrno(PyExc_IOError); 1093 | } else { 1094 | ret = PyBool_FromLong(v); 1095 | } 1096 | } 1097 | return ret; 1098 | } 1099 | 1100 | static PyObject* set_disable_fragments(PyObject* dummy, PyObject* args) 1101 | { 1102 | PyObject* ret = 0; 1103 | int fd, v; 1104 | 1105 | if (PyArg_ParseTuple(args, "ii", &fd, &v)) { 1106 | if (setsockopt(fd, SOL_SCTP, SCTP_DISABLE_FRAGMENTS, &v, sizeof(v))) { 1107 | PyErr_SetFromErrno(PyExc_IOError); 1108 | } else { 1109 | ret = Py_None; Py_INCREF(ret); 1110 | } 1111 | } 1112 | return ret; 1113 | } 1114 | 1115 | static PyObject* get_autoclose(PyObject* dummy, PyObject* args) 1116 | { 1117 | PyObject* ret = 0; 1118 | int fd, v; 1119 | socklen_t lv = sizeof(v); 1120 | 1121 | if (PyArg_ParseTuple(args, "i", &fd)) { 1122 | if (getsockopt(fd, SOL_SCTP, SCTP_AUTOCLOSE, &v, &lv)) { 1123 | PyErr_SetFromErrno(PyExc_IOError); 1124 | } else { 1125 | ret = Py23_PyLong_FromLong(v); 1126 | } 1127 | } 1128 | return ret; 1129 | } 1130 | 1131 | static PyObject* set_autoclose(PyObject* dummy, PyObject* args) 1132 | { 1133 | PyObject* ret = 0; 1134 | int fd, v; 1135 | 1136 | if (PyArg_ParseTuple(args, "ii", &fd, &v)) { 1137 | if (setsockopt(fd, SOL_SCTP, SCTP_AUTOCLOSE, &v, sizeof(v))) { 1138 | PyErr_SetFromErrno(PyExc_IOError); 1139 | } else { 1140 | ret = Py_None; Py_INCREF(ret); 1141 | } 1142 | } 1143 | return ret; 1144 | } 1145 | 1146 | static PyObject* get_adaptation(PyObject* dummy, PyObject* args) 1147 | { 1148 | PyObject* ret = 0; 1149 | int fd, v; 1150 | socklen_t lv = sizeof(v); 1151 | 1152 | if (PyArg_ParseTuple(args, "i", &fd)) { 1153 | if (getsockopt(fd, SOL_SCTP, SCTP_ADAPTATION_LAYER, &v, &lv)) { 1154 | PyErr_SetFromErrno(PyExc_IOError); 1155 | } else { 1156 | ret = Py23_PyLong_FromLong(v); 1157 | } 1158 | } 1159 | return ret; 1160 | } 1161 | 1162 | static PyObject* set_adaptation(PyObject* dummy, PyObject* args) 1163 | { 1164 | PyObject* ret = 0; 1165 | int fd, v; 1166 | 1167 | if (PyArg_ParseTuple(args, "ii", &fd, &v)) { 1168 | if (setsockopt(fd, SOL_SCTP, SCTP_ADAPTATION_LAYER, &v, sizeof(v))) { 1169 | PyErr_SetFromErrno(PyExc_IOError); 1170 | } else { 1171 | ret = Py_None; Py_INCREF(ret); 1172 | } 1173 | } 1174 | return ret; 1175 | } 1176 | 1177 | static PyObject* get_sndbuf(PyObject* dummy, PyObject* args) 1178 | { 1179 | PyObject* ret = 0; 1180 | int fd, v; 1181 | socklen_t lv = sizeof(v); 1182 | 1183 | if (PyArg_ParseTuple(args, "i", &fd)) { 1184 | if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &v, &lv)) { 1185 | PyErr_SetFromErrno(PyExc_IOError); 1186 | } else { 1187 | ret = Py23_PyLong_FromLong(v); 1188 | } 1189 | } 1190 | return ret; 1191 | } 1192 | 1193 | static PyObject* set_sndbuf(PyObject* dummy, PyObject* args) 1194 | { 1195 | PyObject* ret = 0; 1196 | int fd, v; 1197 | 1198 | if (PyArg_ParseTuple(args, "ii", &fd, &v)) { 1199 | if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &v, sizeof(v))) { 1200 | PyErr_SetFromErrno(PyExc_IOError); 1201 | } else { 1202 | ret = Py_None; Py_INCREF(ret); 1203 | } 1204 | } 1205 | return ret; 1206 | } 1207 | 1208 | static PyObject* get_rcvbuf(PyObject* dummy, PyObject* args) 1209 | { 1210 | PyObject* ret = 0; 1211 | int fd, v; 1212 | socklen_t lv = sizeof(v); 1213 | 1214 | if (PyArg_ParseTuple(args, "i", &fd)) { 1215 | if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &v, &lv)) { 1216 | PyErr_SetFromErrno(PyExc_IOError); 1217 | } else { 1218 | ret = Py23_PyLong_FromLong(v); 1219 | } 1220 | } 1221 | return ret; 1222 | } 1223 | 1224 | static PyObject* set_rcvbuf(PyObject* dummy, PyObject* args) 1225 | { 1226 | PyObject* ret = 0; 1227 | int fd, v; 1228 | 1229 | if (PyArg_ParseTuple(args, "ii", &fd, &v)) { 1230 | if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &v, sizeof(v))) { 1231 | PyErr_SetFromErrno(PyExc_IOError); 1232 | } else { 1233 | ret = Py_None; Py_INCREF(ret); 1234 | } 1235 | } 1236 | return ret; 1237 | } 1238 | 1239 | static int to_sockaddr(const char *caddr, int port, struct sockaddr* saddr, int* slen) 1240 | { 1241 | int ret = 1; 1242 | 1243 | *slen = 0; 1244 | 1245 | #ifdef DEBUG 1246 | printf("[DEBUG to_sockaddr] converting caddr: %s, port: %d\n", caddr, port); 1247 | #endif 1248 | 1249 | if (strcmp(caddr, "") == 0) { 1250 | saddr->sa_family = AF_INET; 1251 | ((struct sockaddr_in*) saddr)->sin_addr.s_addr = INADDR_ANY; 1252 | } else if (strcmp(caddr, "") == 0) { 1253 | saddr->sa_family = AF_INET; 1254 | ((struct sockaddr_in*) saddr)->sin_addr.s_addr = INADDR_BROADCAST; 1255 | } else if (inet_pton(AF_INET6, caddr, &(((struct sockaddr_in6*)saddr)->sin6_addr)) > 0) { 1256 | saddr->sa_family = AF_INET6; 1257 | } else if (inet_pton(AF_INET, caddr, &(((struct sockaddr_in*)saddr)->sin_addr)) > 0) { 1258 | saddr->sa_family = AF_INET; 1259 | } else { 1260 | saddr->sa_family = -1; 1261 | ret = 0; 1262 | } 1263 | 1264 | if (saddr->sa_family == AF_INET) { 1265 | ((struct sockaddr_in*) saddr)->sin_port = htons(port); 1266 | *slen = sizeof(struct sockaddr_in); 1267 | } else if (saddr->sa_family == AF_INET6) { 1268 | ((struct sockaddr_in6*) saddr)->sin6_port = htons(port); 1269 | *slen = sizeof(struct sockaddr_in6); 1270 | } 1271 | 1272 | #ifdef DEBUG 1273 | printf("[DEBUG to_sockaddr] sockaddr result is family: 0x%x, s_addr: 0x%x, port: 0x%x\n", 1274 | ((struct sockaddr_in*)saddr)->sin_family, 1275 | ((struct sockaddr_in*)saddr)->sin_addr.s_addr, 1276 | ((struct sockaddr_in*)saddr)->sin_port); 1277 | #endif 1278 | return ret; 1279 | } 1280 | 1281 | static int from_sockaddr(struct sockaddr* saddr, int* family, int* slen, int* port, char* caddr, int cnt) 1282 | { 1283 | int ret = 0; 1284 | *family = saddr->sa_family; 1285 | if (*family == AF_INET) { 1286 | *slen = sizeof(struct sockaddr_in); 1287 | if (inet_ntop(AF_INET, &(((struct sockaddr_in*) saddr)->sin_addr), caddr, cnt)) { 1288 | *port = ntohs(((struct sockaddr_in*) saddr)->sin_port); 1289 | ret = 1; 1290 | } 1291 | } else if (*family == AF_INET6) { 1292 | *slen = sizeof(struct sockaddr_in6); 1293 | if (inet_ntop(AF_INET6, &(((struct sockaddr_in6*) saddr)->sin6_addr), caddr, cnt)) { 1294 | *port = ntohs(((struct sockaddr_in6*) saddr)->sin6_port); 1295 | ret = 1; 1296 | } 1297 | } 1298 | return ret; 1299 | } 1300 | 1301 | static PyObject* _sockaddr_test(PyObject* dummy, PyObject* args) 1302 | { 1303 | PyObject* ret = 0; 1304 | PyObject* addrtupleret = 0; 1305 | char *caddr; 1306 | int port; 1307 | struct sockaddr_storage saddr; 1308 | int slen; 1309 | int family; 1310 | char caddr2[256]; 1311 | 1312 | if (! PyArg_ParseTuple(args, "(si)", &caddr, &port)) { 1313 | return ret; 1314 | } 1315 | 1316 | printf("DEBUG: addr=%s, port=%d\n", caddr, port); 1317 | 1318 | if (! to_sockaddr(caddr, port, (struct sockaddr*) &saddr, &slen)) { 1319 | PyErr_SetString(PyExc_ValueError, "address could not be translated"); 1320 | return ret; 1321 | } 1322 | port = 0; 1323 | caddr = 0; 1324 | 1325 | if (! from_sockaddr((struct sockaddr*) &saddr, &family, &slen, &port, caddr2, sizeof(caddr2))) { 1326 | PyErr_SetString(PyExc_ValueError, "address could not be de-translated"); 1327 | return ret; 1328 | } 1329 | 1330 | ret = PyTuple_New(4); 1331 | addrtupleret = PyTuple_New(2); 1332 | PyTuple_SetItem(ret, 0, Py23_PyUnicode_FromFormat("family %d, size %d, address %s.%d", family, slen, caddr2, port)); 1333 | PyTuple_SetItem(ret, 1, Py23_PyLong_FromLong(family)); 1334 | PyTuple_SetItem(ret, 2, Py23_PyLong_FromLong(slen)); 1335 | PyTuple_SetItem(ret, 3, addrtupleret); 1336 | PyTuple_SetItem(addrtupleret, 0, PyUnicode_FromString(caddr2)); 1337 | PyTuple_SetItem(addrtupleret, 1, Py23_PyLong_FromLong(port)); 1338 | 1339 | return ret; 1340 | } 1341 | 1342 | static PyObject* set_peer_primary(PyObject* dummy, PyObject* args) 1343 | { 1344 | PyObject* ret = 0; 1345 | int fd; 1346 | int l; 1347 | int assoc_id; 1348 | char* addr; 1349 | int port; 1350 | struct sctp_setpeerprim ssp; 1351 | 1352 | if (! PyArg_ParseTuple(args, "ii(si)", &fd, &assoc_id, &addr, &port)) { 1353 | return ret; 1354 | } 1355 | 1356 | bzero(&ssp, sizeof(ssp)); 1357 | ssp.sspp_assoc_id = assoc_id; 1358 | if (! to_sockaddr(addr, port, (struct sockaddr*) &(ssp.sspp_addr), &l)) { 1359 | PyErr_SetString(PyExc_ValueError, "Invalid address"); 1360 | return ret; 1361 | } 1362 | 1363 | if (setsockopt(fd, SOL_SCTP, SCTP_SET_PEER_PRIMARY_ADDR, &ssp, sizeof(ssp))) { 1364 | PyErr_SetFromErrno(PyExc_IOError); 1365 | } else { 1366 | ret = Py_None; Py_INCREF(ret); 1367 | } 1368 | 1369 | return ret; 1370 | } 1371 | 1372 | static PyObject* set_primary(PyObject* dummy, PyObject* args) 1373 | { 1374 | PyObject* ret = 0; 1375 | int fd; 1376 | int l; 1377 | int assoc_id; 1378 | char* addr; 1379 | struct sctp_prim ssp; 1380 | int port; 1381 | 1382 | if (! PyArg_ParseTuple(args, "ii(si)", &fd, &assoc_id, &addr, &port)) { 1383 | return ret; 1384 | } 1385 | 1386 | bzero(&ssp, sizeof(ssp)); 1387 | ssp.ssp_assoc_id = assoc_id; 1388 | if (! to_sockaddr(addr, port, (struct sockaddr*) &(ssp.ssp_addr), &l)) { 1389 | PyErr_SetString(PyExc_ValueError, "Invalid address"); 1390 | return ret; 1391 | } 1392 | 1393 | if (setsockopt(fd, SOL_SCTP, SCTP_PRIMARY_ADDR, &ssp, sizeof(ssp))) { 1394 | PyErr_SetFromErrno(PyExc_IOError); 1395 | } else { 1396 | ret = Py_None; Py_INCREF(ret); 1397 | } 1398 | 1399 | return ret; 1400 | } 1401 | 1402 | static PyObject* bindx(PyObject* dummy, PyObject* args) 1403 | { 1404 | PyObject* ret = 0; 1405 | int fd; 1406 | PyObject* addrs; 1407 | struct sockaddr saddr; 1408 | struct sockaddr* saddrs; 1409 | int saddr_len, saddrs_len; 1410 | int addrcount; 1411 | int flags; 1412 | int x; 1413 | 1414 | if (! PyArg_ParseTuple(args, "iOi", &fd, &addrs, &flags)) { 1415 | return ret; 1416 | } 1417 | 1418 | if (! PySequence_Check(addrs)) { 1419 | PyErr_SetString(PyExc_ValueError, "Second parameter must be a sequence of address/port tuples"); 1420 | return ret; 1421 | } 1422 | 1423 | addrcount = PySequence_Length(addrs); 1424 | if (addrcount <= 0) { 1425 | PyErr_SetString(PyExc_ValueError, "Second parameter must be a non-empty sequence"); 1426 | return ret; 1427 | } 1428 | 1429 | saddrs_len = 0; 1430 | saddrs = (struct sockaddr*) malloc(saddrs_len); 1431 | 1432 | for(x = 0; x < addrcount; ++x) { 1433 | const char* caddr; 1434 | int iport; 1435 | 1436 | PyObject* otuple = PySequence_GetItem(addrs, x); 1437 | 1438 | if (! PyArg_ParseTuple(otuple, "si", &caddr, &iport)) { 1439 | free(saddrs); 1440 | return ret; 1441 | } 1442 | 1443 | if (! to_sockaddr(caddr, iport, &saddr, &saddr_len)) { 1444 | PyErr_Format(PyExc_ValueError, "Invalid address: %s", caddr); 1445 | free(saddrs); 1446 | return ret; 1447 | } 1448 | 1449 | if (saddr_len == 0) { 1450 | PyErr_Format(PyExc_ValueError, "Invalid address family: %s", caddr); 1451 | free(saddrs); 1452 | return ret; 1453 | } 1454 | 1455 | saddrs = realloc(saddrs, saddrs_len + saddr_len); 1456 | memcpy( ((char*) saddrs) + saddrs_len, &saddr, saddr_len); 1457 | saddrs_len += saddr_len; 1458 | 1459 | #ifdef DEBUG 1460 | printf("[DEBUG bindx] x: %d, caddr: %s, iport: %d, saddrs_len: %d\n", 1461 | x, caddr, iport, saddrs_len); 1462 | #endif 1463 | 1464 | } 1465 | 1466 | if (sctp_bindx(fd, saddrs, addrcount, flags)) { 1467 | PyErr_SetFromErrno(PyExc_IOError); 1468 | } else { 1469 | ret = Py_None; Py_INCREF(ret); 1470 | } 1471 | 1472 | free(saddrs); 1473 | return ret; 1474 | } 1475 | 1476 | static PyObject* connectx(PyObject* dummy, PyObject* args) 1477 | { 1478 | PyObject* ret = 0; 1479 | PyObject* dict; 1480 | int fd; 1481 | PyObject* addrs; 1482 | struct sockaddr saddr; 1483 | struct sockaddr* saddrs; 1484 | sctp_assoc_t id; 1485 | int saddr_len, saddrs_len; 1486 | int addrcount; 1487 | int x; 1488 | 1489 | if (! PyArg_ParseTuple(args, "iOO", &fd, &addrs, &dict)) { 1490 | return ret; 1491 | } 1492 | 1493 | if (! PySequence_Check(addrs)) { 1494 | PyErr_SetString(PyExc_ValueError, "Second parameter must be a sequence of address/port tuples"); 1495 | return ret; 1496 | } 1497 | 1498 | addrcount = PySequence_Length(addrs); 1499 | if (addrcount <= 0) { 1500 | PyErr_SetString(PyExc_ValueError, "Second parameter must be a non-empty sequence"); 1501 | return ret; 1502 | } 1503 | 1504 | saddrs_len = 0; 1505 | saddrs = (struct sockaddr*) malloc(saddrs_len); 1506 | 1507 | for(x = 0; x < addrcount; ++x) { 1508 | const char* caddr; 1509 | int iport; 1510 | 1511 | PyObject* otuple = PySequence_GetItem(addrs, x); 1512 | 1513 | if (! PyArg_ParseTuple(otuple, "si", &caddr, &iport)) { 1514 | free(saddrs); 1515 | return ret; 1516 | } 1517 | 1518 | if (! to_sockaddr(caddr, iport, &saddr, &saddr_len)) { 1519 | PyErr_Format(PyExc_ValueError, "Invalid address: %s", caddr); 1520 | free(saddrs); 1521 | return ret; 1522 | } 1523 | 1524 | if (saddr_len == 0) { 1525 | PyErr_Format(PyExc_ValueError, "Invalid address family: %s", caddr); 1526 | free(saddrs); 1527 | return ret; 1528 | } 1529 | 1530 | saddrs = realloc(saddrs, saddrs_len + saddr_len); 1531 | memcpy( ((char*) saddrs) + saddrs_len, &saddr, saddr_len); 1532 | saddrs_len += saddr_len; 1533 | } 1534 | 1535 | if (sctp_connectx(fd, saddrs, addrcount, &id)) { 1536 | PyErr_SetFromErrno(PyExc_IOError); 1537 | } else { 1538 | if(PyDict_Check(dict)) PyDict_SetItemString(dict, "assoc_id", Py23_PyLong_FromLong(id)); 1539 | ret = Py_None; Py_INCREF(ret); 1540 | } 1541 | 1542 | free(saddrs); 1543 | return ret; 1544 | } 1545 | 1546 | static PyObject* getpaddrs(PyObject* dummy, PyObject* args) 1547 | { 1548 | PyObject* ret = 0; 1549 | int fd; 1550 | int assoc_id; 1551 | struct sockaddr* saddrs; 1552 | int count; 1553 | int x; 1554 | char addr[256]; 1555 | 1556 | if (! PyArg_ParseTuple(args, "ii", &fd, &assoc_id)) { 1557 | return ret; 1558 | } 1559 | 1560 | count = sctp_getpaddrs(fd, assoc_id, &saddrs); 1561 | 1562 | if (count < 0) { 1563 | PyErr_SetFromErrno(PyExc_IOError); 1564 | } else { 1565 | if (count == 0) { 1566 | saddrs = 0; 1567 | } 1568 | 1569 | ret = PyTuple_New(count); 1570 | char *p = (char*) saddrs; 1571 | 1572 | for(x = 0; x < count; ++x) { 1573 | int len; 1574 | int family; 1575 | int port; 1576 | PyObject* oaddr; 1577 | 1578 | if (from_sockaddr((struct sockaddr*) p, &family, &len, &port, 1579 | addr, sizeof(addr))) { 1580 | oaddr = PyTuple_New(2); 1581 | PyTuple_SetItem(oaddr, 0, PyUnicode_FromString(addr)); 1582 | PyTuple_SetItem(oaddr, 1, Py23_PyLong_FromLong(port)); 1583 | PyTuple_SetItem(ret, x, oaddr); 1584 | } else { 1585 | // something's wrong; not safe to continue 1586 | break; 1587 | } 1588 | 1589 | p += len; 1590 | } 1591 | 1592 | sctp_freepaddrs(saddrs); 1593 | 1594 | // If something went wrong, the remaining expected addresses will be set to None. 1595 | for(; x < count; ++x) { 1596 | PyTuple_SetItem(ret, x, Py_None); 1597 | Py_INCREF(Py_None); 1598 | 1599 | } 1600 | } 1601 | 1602 | return ret; 1603 | } 1604 | 1605 | static PyObject* getladdrs(PyObject* dummy, PyObject* args) 1606 | { 1607 | PyObject* ret = 0; 1608 | int fd; 1609 | int assoc_id; 1610 | struct sockaddr* saddrs; 1611 | int count; 1612 | int x; 1613 | char addr[256]; 1614 | 1615 | if (! PyArg_ParseTuple(args, "ii", &fd, &assoc_id)) { 1616 | return ret; 1617 | } 1618 | 1619 | count = sctp_getladdrs(fd, assoc_id, &saddrs); 1620 | 1621 | if (count < 0) { 1622 | PyErr_SetFromErrno(PyExc_IOError); 1623 | } else { 1624 | if (count == 0) { 1625 | saddrs = 0; 1626 | } 1627 | 1628 | ret = PyTuple_New(count); 1629 | char *p = (char*) saddrs; 1630 | 1631 | for(x = 0; x < count; ++x) { 1632 | int len; 1633 | int family; 1634 | int port; 1635 | PyObject* oaddr; 1636 | 1637 | if (from_sockaddr((struct sockaddr*) p, &family, &len, &port, 1638 | addr, sizeof(addr))) { 1639 | oaddr = PyTuple_New(2); 1640 | PyTuple_SetItem(oaddr, 0, PyUnicode_FromString(addr)); 1641 | PyTuple_SetItem(oaddr, 1, Py23_PyLong_FromLong(port)); 1642 | PyTuple_SetItem(ret, x, oaddr); 1643 | } else { 1644 | // something's wrong; not safe to continue 1645 | break; 1646 | } 1647 | 1648 | p += len; 1649 | } 1650 | 1651 | sctp_freeladdrs(saddrs); 1652 | 1653 | // If something went wrong, the remaining expected addresses will be set to None. 1654 | for(; x < count; ++x) { 1655 | PyTuple_SetItem(ret, x, Py_None); 1656 | Py_INCREF(Py_None); 1657 | 1658 | } 1659 | } 1660 | 1661 | return ret; 1662 | } 1663 | 1664 | static PyObject* sctp_send_msg(PyObject* dummy, PyObject* args) 1665 | { 1666 | int fd, msg_len, size_sent, ppid, flags, stream, ttl, context; 1667 | const char *msg; 1668 | char *to; 1669 | int port; 1670 | 1671 | struct sockaddr_storage sto; 1672 | struct sockaddr_storage *psto = &sto; 1673 | int sto_len; 1674 | 1675 | PyObject *ret = 0; 1676 | 1677 | if (! PyArg_ParseTuple(args, "is#(si)iiiii", &fd, &msg, &msg_len, &to, &port, 1678 | &ppid, &flags, &stream, &ttl, &context)) { 1679 | return ret; 1680 | } 1681 | 1682 | if (msg_len <= 0 && (! (flags & MSG_EOF))) { 1683 | PyErr_SetString(PyExc_ValueError, "Empty messages are not allowed, except if coupled with the MSG_EOF flag."); 1684 | return ret; 1685 | } 1686 | 1687 | if (strlen(to) == 0) { 1688 | // special case: should pass NULL 1689 | sto_len = 0; 1690 | psto = 0; 1691 | } else { 1692 | if (! to_sockaddr(to, port, (struct sockaddr*) psto, &sto_len)) { 1693 | PyErr_SetString(PyExc_ValueError, "Invalid Address"); 1694 | return ret; 1695 | } 1696 | } 1697 | 1698 | Py_BEGIN_ALLOW_THREADS 1699 | size_sent = sctp_sendmsg(fd, msg, msg_len, (struct sockaddr*) psto, sto_len, ppid, 1700 | flags, stream, ttl, context); 1701 | Py_END_ALLOW_THREADS 1702 | 1703 | if (size_sent < 0) { 1704 | PyErr_SetFromErrno(PyExc_IOError); 1705 | return ret; 1706 | } 1707 | 1708 | ret = Py23_PyLong_FromLong(size_sent); 1709 | return ret; 1710 | } 1711 | 1712 | void interpret_sndrcvinfo(PyObject* dict, const struct sctp_sndrcvinfo* sinfo) 1713 | { 1714 | PyDict_SetItemString(dict, "stream", Py23_PyLong_FromLong(sinfo->sinfo_stream)); 1715 | PyDict_SetItemString(dict, "ssn", Py23_PyLong_FromLong(sinfo->sinfo_ssn)); 1716 | PyDict_SetItemString(dict, "flags", Py23_PyLong_FromLong(sinfo->sinfo_flags)); 1717 | PyDict_SetItemString(dict, "ppid", Py23_PyLong_FromLong(sinfo->sinfo_ppid)); 1718 | PyDict_SetItemString(dict, "context", Py23_PyLong_FromLong(sinfo->sinfo_context)); 1719 | PyDict_SetItemString(dict, "timetolive", Py23_PyLong_FromLong(sinfo->sinfo_timetolive)); 1720 | PyDict_SetItemString(dict, "tsn", Py23_PyLong_FromLong(sinfo->sinfo_tsn)); 1721 | PyDict_SetItemString(dict, "cumtsn", Py23_PyLong_FromLong(sinfo->sinfo_cumtsn)); 1722 | PyDict_SetItemString(dict, "assoc_id", Py23_PyLong_FromLong(sinfo->sinfo_assoc_id)); 1723 | } 1724 | 1725 | void interpret_notification(PyObject* dict, const void *pnotif, int size) 1726 | { 1727 | const union sctp_notification *notif = pnotif; 1728 | PyDict_SetItemString(dict, "type", Py23_PyLong_FromLong(notif->sn_header.sn_type)); 1729 | PyDict_SetItemString(dict, "flags", Py23_PyLong_FromLong(notif->sn_header.sn_flags)); 1730 | PyDict_SetItemString(dict, "length", Py23_PyLong_FromLong(notif->sn_header.sn_length)); 1731 | 1732 | switch (notif->sn_header.sn_type) { 1733 | case SCTP_ASSOC_CHANGE: 1734 | { 1735 | const struct sctp_assoc_change* n = &(notif->sn_assoc_change); 1736 | PyDict_SetItemString(dict, "state", Py23_PyLong_FromLong(n->sac_state)); 1737 | PyDict_SetItemString(dict, "error", Py23_PyLong_FromLong(n->sac_error)); 1738 | PyDict_SetItemString(dict, "outbound_streams", Py23_PyLong_FromLong(n->sac_outbound_streams)); 1739 | PyDict_SetItemString(dict, "inbound_streams", Py23_PyLong_FromLong(n->sac_inbound_streams)); 1740 | PyDict_SetItemString(dict, "assoc_id", Py23_PyLong_FromLong(n->sac_assoc_id)); 1741 | } 1742 | break; 1743 | case SCTP_PEER_ADDR_CHANGE: 1744 | { 1745 | const struct sctp_paddr_change* n = &(notif->sn_paddr_change); 1746 | char caddr[256]; 1747 | int family; 1748 | int len; 1749 | int port; 1750 | PyObject* oaddr; 1751 | 1752 | if (from_sockaddr((struct sockaddr*) &(n->spc_aaddr), &family, &len, &port, 1753 | caddr, sizeof(caddr))) { 1754 | oaddr = PyTuple_New(2); 1755 | PyTuple_SetItem(oaddr, 0, PyUnicode_FromString(caddr)); 1756 | PyTuple_SetItem(oaddr, 1, Py23_PyLong_FromLong(port)); 1757 | } else { 1758 | // something went wrong 1759 | oaddr = Py_None; 1760 | Py_INCREF(Py_None); 1761 | } 1762 | 1763 | PyDict_SetItemString(dict, "addr", oaddr); 1764 | PyDict_SetItemString(dict, "state", Py23_PyLong_FromLong(n->spc_state)); 1765 | PyDict_SetItemString(dict, "error", Py23_PyLong_FromLong(n->spc_error)); 1766 | PyDict_SetItemString(dict, "assoc_id", Py23_PyLong_FromLong(n->spc_assoc_id)); 1767 | } 1768 | break; 1769 | case SCTP_SEND_FAILED: 1770 | { 1771 | const struct sctp_send_failed* n = &(notif->sn_send_failed); 1772 | const char* cdata = ((char*) notif) + sizeof(struct sctp_send_failed); 1773 | int ldata = size - sizeof(struct sctp_send_failed); 1774 | 1775 | if (ldata >= 0) { 1776 | PyObject* info = PyDict_New(); 1777 | interpret_sndrcvinfo(info, &(n->ssf_info)); 1778 | PyDict_SetItemString(dict, "_info", info); 1779 | PyDict_SetItemString(dict, "error", Py23_PyLong_FromLong(n->ssf_error)); 1780 | PyDict_SetItemString(dict, "assoc_id", Py23_PyLong_FromLong(n->ssf_assoc_id)); 1781 | PyDict_SetItemString(dict, "data", PyUnicode_FromStringAndSize(cdata, ldata)); 1782 | } 1783 | } 1784 | break; 1785 | case SCTP_REMOTE_ERROR: 1786 | { 1787 | const struct sctp_remote_error* n = &(notif->sn_remote_error); 1788 | const char* cdata = ((char*) notif) + sizeof(struct sctp_remote_error); 1789 | int ldata = size - sizeof(struct sctp_remote_error); 1790 | 1791 | if (ldata >= 0) { 1792 | PyDict_SetItemString(dict, "error", Py23_PyLong_FromLong(n->sre_error)); 1793 | PyDict_SetItemString(dict, "assoc_id", Py23_PyLong_FromLong(n->sre_assoc_id)); 1794 | PyDict_SetItemString(dict, "data", PyUnicode_FromStringAndSize(cdata, ldata)); 1795 | } 1796 | } 1797 | break; 1798 | case SCTP_SHUTDOWN_EVENT: 1799 | { 1800 | const struct sctp_shutdown_event* n = &(notif->sn_shutdown_event); 1801 | PyDict_SetItemString(dict, "assoc_id", Py23_PyLong_FromLong(n->sse_assoc_id)); 1802 | } 1803 | break; 1804 | case SCTP_PARTIAL_DELIVERY_EVENT: 1805 | { 1806 | const struct sctp_pdapi_event* n = &(notif->sn_pdapi_event); 1807 | PyDict_SetItemString(dict, "indication", Py23_PyLong_FromLong(n->pdapi_indication)); 1808 | PyDict_SetItemString(dict, "assoc_id", Py23_PyLong_FromLong(n->pdapi_assoc_id)); 1809 | } 1810 | break; 1811 | case SCTP_ADAPTATION_INDICATION: 1812 | { 1813 | const struct sctp_adaptation_event* n = &(notif->sn_adaptation_event); 1814 | PyDict_SetItemString(dict, "adaptation_ind", Py23_PyLong_FromLong(n->sai_adaptation_ind)); 1815 | PyDict_SetItemString(dict, "assoc_id", Py23_PyLong_FromLong(n->sai_assoc_id)); 1816 | } 1817 | break; 1818 | } 1819 | } 1820 | 1821 | static PyObject* sctp_recv_msg(PyObject* dummy, PyObject* args) 1822 | { 1823 | int fd; 1824 | size_t max_len; 1825 | 1826 | struct sockaddr_storage sfrom; 1827 | socklen_t sfrom_len = sizeof(sfrom); 1828 | int family; 1829 | int len; 1830 | int port; 1831 | char cfrom[256]; 1832 | char *msg; 1833 | int size; 1834 | int flags = 0; 1835 | struct sctp_sndrcvinfo sinfo; 1836 | 1837 | PyObject* notification = PyDict_New(); 1838 | PyObject* ret = 0; 1839 | PyObject* oaddr = 0; 1840 | 1841 | if (! PyArg_ParseTuple(args, "in", &fd, &max_len)) { 1842 | return ret; 1843 | } 1844 | 1845 | msg = malloc(max_len); 1846 | if (! msg) { 1847 | PyErr_SetString(PyExc_MemoryError, "Out of memory, malloc() failed"); 1848 | return ret; 1849 | } 1850 | 1851 | bzero(&sfrom, sizeof(sfrom)); 1852 | bzero(&sinfo, sizeof(sinfo)); 1853 | 1854 | Py_BEGIN_ALLOW_THREADS 1855 | size = sctp_recvmsg(fd, msg, max_len, (struct sockaddr*) &sfrom, &sfrom_len, &sinfo, &flags); 1856 | Py_END_ALLOW_THREADS 1857 | 1858 | if (size < 0) { 1859 | free(msg); 1860 | PyErr_SetFromErrno(PyExc_IOError); 1861 | return ret; 1862 | } 1863 | 1864 | if (flags & MSG_NOTIFICATION) { 1865 | interpret_notification(notification, msg, size); 1866 | size = -1; 1867 | } else { 1868 | interpret_sndrcvinfo(notification, &sinfo); 1869 | } 1870 | 1871 | if (from_sockaddr((struct sockaddr*) &sfrom, &family, &len, &port, cfrom, sizeof(cfrom))) { 1872 | oaddr = PyTuple_New(2); 1873 | PyTuple_SetItem(oaddr, 0, PyUnicode_FromString(cfrom)); 1874 | PyTuple_SetItem(oaddr, 1, Py23_PyLong_FromLong(port)); 1875 | } else { 1876 | // something went wrong 1877 | oaddr = Py_None; 1878 | Py_INCREF(Py_None); 1879 | } 1880 | 1881 | ret = PyTuple_New(4); 1882 | PyTuple_SetItem(ret, 0, oaddr); 1883 | PyTuple_SetItem(ret, 1, Py23_PyLong_FromLong(flags)); 1884 | if (size >= 0) { 1885 | PyTuple_SetItem(ret, 2, PyBytes_FromStringAndSize(msg, size)); 1886 | } else { 1887 | PyTuple_SetItem(ret, 2, Py_None); 1888 | Py_INCREF(Py_None); 1889 | } 1890 | PyTuple_SetItem(ret, 3, notification); 1891 | 1892 | free(msg); 1893 | return ret; 1894 | } 1895 | 1896 | -------------------------------------------------------------------------------- /_sctp.h: -------------------------------------------------------------------------------- 1 | /* SCTP bindings for Python 2 | * 3 | * _sctp.h: paliative C-side definitions 4 | * 5 | * This library is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU Lesser General Public License as published by the 7 | * Free Software Foundation; either version 2.1 of the License, or (at your 8 | * option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, but WITHOUT 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 12 | * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 13 | * details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public License 16 | * along with this library; If not, see . 17 | * 18 | * Philippe Langlois (Philippe.Langlois@gmail.com) 19 | * Copyright (c) 2009 Philippe Langlois 20 | */ 21 | 22 | #ifndef IPPROTO_SCTP 23 | #define IPPROTO_SCTP 132 24 | #endif // IPPROTO_SCTP 25 | 26 | #ifndef SOL_SCTP 27 | #define SOL_SCTP 132 28 | #endif 29 | 30 | #ifndef MSG_FIN 31 | #define MSG_FIN 0x200 32 | #endif 33 | 34 | #ifndef MSG_NOTIFICATION 35 | #define MSG_NOTIFICATION 0x8000 36 | #endif 37 | 38 | /* Notification error codes */ 39 | #ifndef SCTP_NOTIFY_DATAGRAM_UNSENT 40 | #define SCTP_NOTIFY_DATAGRAM_UNSENT 0x0001 41 | #endif 42 | #ifndef SCTP_NOTIFY_DATAGRAM_SENT 43 | #define SCTP_NOTIFY_DATAGRAM_SENT 0x0002 44 | #endif 45 | #ifndef SCTP_FAILED_THRESHOLD 46 | #define SCTP_FAILED_THRESHOLD 0x0004 47 | #endif 48 | #ifndef SCTP_HEARTBEAT_SUCCESS 49 | #define SCTP_HEARTBEAT_SUCCESS 0x0008 50 | #endif 51 | #ifndef SCTP_RESPONSE_TO_USER_REQ 52 | #define SCTP_RESPONSE_TO_USER_REQ 0x000f 53 | #endif 54 | #ifndef SCTP_INTERNAL_ERROR 55 | #define SCTP_INTERNAL_ERROR 0x0010 56 | #endif 57 | #ifndef SCTP_SHUTDOWN_GUARD_EXPIRES 58 | #define SCTP_SHUTDOWN_GUARD_EXPIRES 0x0020 59 | #endif 60 | #ifndef SCTP_RECEIVED_SACK 61 | #define SCTP_RECEIVED_SACK 0x0040 62 | #endif 63 | #ifndef SCTP_PEER_FAULTY 64 | #define SCTP_PEER_FAULTY 0x0080 65 | #endif 66 | 67 | /* SCTP Association states. */ 68 | #ifndef SCTP_EMPTY 69 | #define SCTP_EMPTY 0 70 | #endif 71 | #ifndef SCTP_CLOSED 72 | #define SCTP_CLOSED 1 73 | #endif 74 | 75 | #ifndef SCTP_SN_TYPE_BASE 76 | #define SCTP_SN_TYPE_BASE (1<<15) 77 | #endif 78 | 79 | #ifndef linux 80 | /* 81 | * 7.1.10 Set Primary Address (SCTP_PRIMARY_ADDR) 82 | * 83 | * Requests that the local SCTP stack use the enclosed peer address as 84 | * the association primary. The enclosed address must be one of the 85 | * association peer's addresses. The following structure is used to 86 | * make a set peer primary request: 87 | */ 88 | struct sctp_prim { 89 | sctp_assoc_t ssp_assoc_id; 90 | struct sockaddr_storage ssp_addr; 91 | }; 92 | #endif 93 | -------------------------------------------------------------------------------- /sctp.py: -------------------------------------------------------------------------------- 1 | # SCTP bindings for Python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Python-side bindings 5 | # 6 | # This library is free software; you can redistribute it and/or modify it 7 | # under the terms of the GNU Lesser General Public License as published by the 8 | # Free Software Foundation; either version 2.1 of the License, or (at your 9 | # option) any later version. 10 | # 11 | # This library is distributed in the hope that it will be useful, but WITHOUT 12 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 14 | # details. 15 | # 16 | # You should have received a copy of the GNU Lesser General Public License 17 | # along with this library; If not, see . 18 | # 19 | # Elvis Pfützenreuter (elvis.pfutzenreuter@{gmail.com,indt.org.br}) 20 | # Copyright (c) 2005 Instituto Nokia de Tecnologia 21 | 22 | """ 23 | Python bindings for SCTP. It is compatible with kernel-level SCTP 24 | implementations with BSD/Sockets API. 25 | 26 | For the users, the relevant classes and functions are: 27 | 28 | features() 29 | sctpsocket(): base class, ought not used directly, althrough it can be 30 | sctpsocket_tcp(): TCP-style subclass 31 | sctpsocket_udp(): UDP-style subclass 32 | 33 | SCTP sockets do NOT inherit from socket._socketobject, instead they 34 | CONTAIN a standard Python socket, and DELEGATE unknown calls to it. 35 | So it should fit in most places where a "real" socket is expected. 36 | 37 | When receiving data, it is also possible to receive metadata in the 38 | form of events. An event will be one of the following classes: 39 | 40 | sndrcvinfo(): 41 | notification(): 42 | assoc_change(): 43 | paddr_change(): 44 | send_failed(): 45 | remote_error(): 46 | shutdown_event(): 47 | pdapi_event(): 48 | adaptation_event(): 49 | 50 | Every SCTP socket has a number of properties. Two "complex" properties, 51 | that contain a number of sub-properties, are: 52 | 53 | sctpsocket.events = event_subscribe() 54 | sctpsocket.initparams = initparams() 55 | 56 | Please take a look in the classes' documentation to learn more about 57 | respective sub-properties. 58 | 59 | Most SCTP-specific macros are available as constants in Python, so 60 | an experienced SCTP/C API programmer will feel at home at pysctp. 61 | The only difference is that constants used only inside a particular 62 | event, are defined as class constants and not as module constants, 63 | to avoid excessive namespace pollution. 64 | 65 | """ 66 | 67 | from __future__ import print_function 68 | 69 | import socket 70 | import _sctp 71 | 72 | import datetime 73 | import time 74 | import os 75 | import sys 76 | 77 | ####################################### CONSTANTS 78 | 79 | # bindx() constants 80 | BINDX_ADD = _sctp.getconstant("BINDX_ADD") 81 | BINDX_REMOVE = _sctp.getconstant("BINDX_REMOVE") 82 | BINDX_REM = BINDX_REMOVE 83 | 84 | # high-level (sndrcvinfo) message flags 85 | MSG_UNORDERED = _sctp.getconstant("MSG_UNORDERED") 86 | MSG_ADDR_OVER = _sctp.getconstant("MSG_ADDR_OVER") 87 | MSG_ABORT = _sctp.getconstant("MSG_ABORT") 88 | MSG_EOF = _sctp.getconstant("MSG_EOF") 89 | MSG_FIN = _sctp.getconstant("MSG_FIN") 90 | MSG_SENDALL = _sctp.getconstant("MSG_SENDALL") 91 | MSG_ADDR_OVERRIDE = MSG_ADDR_OVER 92 | 93 | # low-level (sendto/sendmsg) flags 94 | FLAG_NOTIFICATION = _sctp.getconstant("MSG_NOTIFICATION") 95 | FLAG_EOR = _sctp.getconstant("MSG_EOR") 96 | FLAG_DONTROUTE = _sctp.getconstant("MSG_DONTROUTE") 97 | 98 | (HAVE_SCTP, HAVE_KERNEL_SCTP, HAVE_SCTP_MULTIBUF, HAVE_SCTP_NOCONNECT, \ 99 | HAVE_SCTP_PRSCTP, HAVE_SCTP_ADDIP, HAVE_SCTP_CANSET_PRIMARY, HAVE_SCTP_SAT_NETWORK_CAPABILITY) = \ 100 | (1,2,4,8,16,32,64,128) 101 | 102 | # socket constants 103 | SOL_SCTP = _sctp.getconstant("SOL_SCTP") 104 | IPPROTO_SCTP = _sctp.getconstant("IPPROTO_SCTP") 105 | SOCK_SEQPACKET = _sctp.getconstant("SOCK_SEQPACKET") 106 | SOCK_STREAM = _sctp.getconstant("SOCK_STREAM") 107 | (STYLE_TCP, STYLE_UDP) = (SOCK_STREAM, SOCK_SEQPACKET) 108 | (TCP_STYLE, UDP_STYLE) = (STYLE_TCP, STYLE_UDP) 109 | 110 | 111 | ####################################### STRUCTURES FOR SCTP MESSAGES AND EVENTS 112 | 113 | class initmsg(object): 114 | """ 115 | Object used in explicit opening of SCTP associations in 116 | UDP-style sockets without passing any user-data message 117 | (UNIMPLEMENTED in pysctp, probably needs direct sendmsg() use) 118 | 119 | User will be better off it instantiate this class via 120 | initparams.initmsg() constructor, because it fills initmsg with 121 | socket's defaults. 122 | """ 123 | def __init__(self): 124 | self.num_ostreams = 0 125 | self.max_instreams = 0 126 | self.max_attempts = 0 127 | self.max_init_timeo = 0 128 | 129 | class initparams(object): 130 | """ 131 | SCTP default initialization parameters. New associations are opened 132 | with the properties specified in this object. 133 | 134 | Properties are: 135 | 136 | num_ostreams 137 | max_instreams 138 | max_attempts 139 | max_init_timeo (in seconds) 140 | 141 | Setting of properties will automatically call flush() (that will 142 | setsockopt the socket) unless "autoflush" is set to False. 143 | 144 | User should never need to instantiate this class directly. For every 145 | property, there is a pair of get_P and set_P methods, that user can, 146 | but should not need, to call directly. 147 | """ 148 | 149 | def __init__(self, container): 150 | """ 151 | The container object passed as parameter will (I hope) be a 152 | sctpsocket() descendant. It should offer the _get_initparams() 153 | and _set_initparams() for the socket it represents. 154 | """ 155 | self.autoflush = True 156 | self.container = container 157 | self._num_ostreams = self._max_instreams = 0 158 | self._max_attempts = self._max_init_timeo = 0 159 | self.__dict__.update(self.container._get_initparams()) 160 | 161 | def flush(self): 162 | """ 163 | Flushes the initialization properties to the socket 164 | (setsockopt). If autoflush equals True (the default), 165 | any change to properties will call this automatically. 166 | """ 167 | self.container._set_initparams(self.__dict__) 168 | 169 | def initmsg(self): 170 | """ 171 | Builds a initmsg() object containing the same properties 172 | as the socket initalization parameters. The initmsg() 173 | can then be changed and used to open a particular SCTP 174 | association. 175 | """ 176 | r = initmsg() 177 | r.num_ostreams = self._num_ostreams; 178 | r.max_instreams = self._max_instreams; 179 | r.max_attempts = self._max_attempts; 180 | r.max_init_timeo = self._max_init_timeo; 181 | return r 182 | 183 | def get_num_ostreams(self): 184 | return self._num_ostreams; 185 | 186 | def set_num_ostreams(self, v): 187 | self._num_ostreams = v; 188 | if self.autoflush: 189 | self.flush() 190 | 191 | def get_max_instreams(self): 192 | return self._max_instreams; 193 | 194 | def set_max_instreams(self, v): 195 | self._max_instreams = v; 196 | if self.autoflush: 197 | self.flush() 198 | 199 | def get_max_attempts(self): 200 | return self._max_attempts; 201 | 202 | def set_max_attempts(self, v): 203 | self._max_attempts = v; 204 | if self.autoflush: 205 | self.flush() 206 | 207 | def get_max_init_timeo(self): 208 | return self._max_init_timeo; 209 | 210 | def set_max_init_timeo(self, v): 211 | self._max_init_timeo = v; 212 | if self.autoflush: 213 | self.flush() 214 | 215 | num_ostreams = property(get_num_ostreams, set_num_ostreams) 216 | max_instreams = property(get_max_instreams, set_max_instreams) 217 | max_attempts = property(get_max_attempts, set_max_attempts) 218 | max_init_timeo = property(get_max_init_timeo, set_max_init_timeo) 219 | 220 | class sndrcvinfo(object): 221 | """ 222 | Send/receive ancilliary data. In PySCTP, this object is only used 223 | to *receive* ancilliary information about a message. The user 224 | should never need to instantiate this class. 225 | 226 | The "flag" attribute is a bitmap of the MSG_* class constants. 227 | """ 228 | 229 | def __init__(self, values=None): 230 | self.stream = 0 231 | self.ssn = 0 232 | self.flags = 0 233 | self.context = 0 234 | self.timetolive = 0 235 | self.tsn = 0 236 | self.cumtsn = 0 237 | self.assoc_id = 0 238 | if values: 239 | self.__dict__.update(values) 240 | 241 | class notification(object): 242 | """ 243 | Base class for all notification objects. Objects of this particular 244 | type should never appear in message receiving. If it does, it means 245 | that PySCTP received a notification it does not understand (probably 246 | a "new" event type), and author must be warned about this. 247 | 248 | The "type" and "flags" fields belong to this class because they 249 | appear in every notification. This class contains "type_*" constants 250 | that identify every notification type. On the other hand, "flags" 251 | meanings are particular for every notification type. 252 | 253 | The user should never need to instantiate this class. 254 | """ 255 | def __init__(self, values): 256 | self.type = 0 257 | self.flags = 0 258 | if values: 259 | self.__dict__.update(values) 260 | 261 | type_SN_TYPE_BASE = _sctp.getconstant("SCTP_SN_TYPE_BASE") 262 | type_ASSOC_CHANGE = _sctp.getconstant("SCTP_ASSOC_CHANGE") 263 | type_PEER_ADDR_CHANGE = _sctp.getconstant("SCTP_PEER_ADDR_CHANGE") 264 | type_SEND_FAILED = _sctp.getconstant("SCTP_SEND_FAILED") 265 | type_REMOTE_ERROR = _sctp.getconstant("SCTP_REMOTE_ERROR") 266 | type_SHUTDOWN_EVENT = _sctp.getconstant("SCTP_SHUTDOWN_EVENT") 267 | type_PARTIAL_DELIVERY_EVENT = _sctp.getconstant("SCTP_PARTIAL_DELIVERY_EVENT") 268 | type_ADAPTATION_INDICATION = _sctp.getconstant("SCTP_ADAPTATION_INDICATION") 269 | 270 | class assoc_change(notification): 271 | """ 272 | Association change notification. The relevant values are: 273 | 274 | state 275 | error 276 | outbound_streams 277 | inbound_streams 278 | assoc_id 279 | 280 | It seems to lack remote address/port pair, but remember that recvmsg() and 281 | sctp_recv() return the address for every (either data or event) message, so 282 | the application can have full knowledge about the new connection. 283 | 284 | Most UDP-style socket users will want to save this event, in particular "assoc_id" 285 | because that value will be needed to further refer to the association. Address/pair 286 | port can also be used to identify the remote peer, but it can be ambiguous since 287 | SCTP can make multihomed associations. 288 | 289 | The "state" can be tested against "state_*" class constants. If state is 290 | state_CANT_START_ASSOCIATION, "error" will be one of the "error_*" constants. 291 | 292 | The user should never need to instantiate this directly. This notification will 293 | be received only if user subscribed to receive that (see event_subscribe class 294 | for details). 295 | """ 296 | def __init__(self, values=None): 297 | self.state = 0 298 | self.error = 0 299 | self.outbound_streams = 0 300 | self.inbound_streams = 0 301 | self.assoc_id = 0 302 | notification.__init__(self, values) 303 | 304 | state_COMM_UP = _sctp.getconstant("SCTP_COMM_UP") 305 | state_COMM_LOST = _sctp.getconstant("SCTP_COMM_LOST") 306 | state_RESTART = _sctp.getconstant("SCTP_RESTART") 307 | state_SHUTDOWN_COMP = _sctp.getconstant("SCTP_SHUTDOWN_COMP") 308 | state_CANT_STR_ASSOC = _sctp.getconstant("SCTP_CANT_STR_ASSOC") 309 | state_CANT_START_ASSOCIATION = _sctp.getconstant("SCTP_CANT_STR_ASSOC") 310 | 311 | error_FAILED_THRESHOLD = _sctp.getconstant("SCTP_FAILED_THRESHOLD") 312 | error_RECEIVED_SACK = _sctp.getconstant("SCTP_RECEIVED_SACK") 313 | error_HEARTBEAT_SUCCESS = _sctp.getconstant("SCTP_HEARTBEAT_SUCCESS") 314 | error_RESPONSE_TO_USER_REQ = _sctp.getconstant("SCTP_RESPONSE_TO_USER_REQ") 315 | error_INTERNAL_ERROR = _sctp.getconstant("SCTP_INTERNAL_ERROR") 316 | error_SHUTDOWN_GUARD_EXPIRES = _sctp.getconstant("SCTP_SHUTDOWN_GUARD_EXPIRES") 317 | error_PEER_FAULTY = _sctp.getconstant("SCTP_PEER_FAULTY") 318 | 319 | class paddr_change(notification): 320 | """ 321 | Peer address change notification. This event is received when a multihomed remote 322 | peer changes the network interface in use. 323 | 324 | The user should never need to instantiate this directly. This 325 | notification will be received only if user subscribed to receive 326 | that (see event_subscribe class for details). 327 | """ 328 | def __init__(self, values=None): 329 | self.addr = ("",0) 330 | self.state = 0 331 | self.error = 0 332 | self.assoc_id = 0 333 | notification.__init__(self, values) 334 | 335 | state_ADDR_AVAILABLE = _sctp.getconstant("SCTP_ADDR_AVAILABLE") 336 | state_ADDR_UNREACHABLE = _sctp.getconstant("SCTP_ADDR_UNREACHABLE") 337 | state_ADDR_REMOVED = _sctp.getconstant("SCTP_ADDR_REMOVED") 338 | state_ADDR_ADDED = _sctp.getconstant("SCTP_ADDR_ADDED") 339 | state_ADDR_MADE_PRIM = _sctp.getconstant("SCTP_ADDR_MADE_PRIM") 340 | 341 | error_FAILED_THRESHOLD = assoc_change.error_FAILED_THRESHOLD 342 | error_RECEIVED_SACK = assoc_change.error_RECEIVED_SACK 343 | error_HEARTBEAT_SUCCESS = assoc_change.error_HEARTBEAT_SUCCESS 344 | error_RESPONSE_TO_USER_REQ = assoc_change.error_RESPONSE_TO_USER_REQ 345 | error_INTERNAL_ERROR = assoc_change.error_INTERNAL_ERROR 346 | error_SHUTDOWN_GUARD_EXPIRES = assoc_change.error_SHUTDOWN_GUARD_EXPIRES 347 | error_PEER_FAULTY = assoc_change.error_PEER_FAULTY 348 | 349 | class remote_error(notification): 350 | """ 351 | Remote error notification. This is received when the remote application 352 | explicitely sends a SCTP_REMOTE_ERROR notification. If the application 353 | protocol does not use this feature, such messages don't need to be 354 | handled. 355 | 356 | It will NOT be received when an "actual", or low-level, error occurs. 357 | For such errors, assoc_change() objects are passed instead. 358 | 359 | The user should never need to instantiate this directly. This 360 | notification will be received only if user subscribed to receive 361 | that (see event_subscribe class for details). 362 | """ 363 | def __init__(self, values=None): 364 | self.error = 0 365 | self.assoc_id = 0 366 | self.data = "" 367 | notification.__init__(self, values) 368 | 369 | class send_failed(notification): 370 | """ 371 | Send error notification. This is received when a particular message could not 372 | be sent. 373 | 374 | When an association fails, all pending messages will be returned as send_failed()s 375 | *after* assoc_change() notification is reveived. Also, send_failed() is returned 376 | when the message time-to-live expires (which is not a fatal error). 377 | 378 | The "flag" attribute can have one of the flag_* class constants. More details about 379 | the message, including the association ID, can be obtained inside the "info" 380 | attribute, that is a sndrcvinfo() object. See sndrcvinfo class documentation for 381 | more details. 382 | 383 | The user should never need to instantiate this directly. This 384 | notification will be received only if user subscribed to receive 385 | that (see event_subscribe class for details). 386 | """ 387 | def __init__(self, values=None): 388 | self.error = 0 389 | self.assoc_id = 0 390 | self.data = "" 391 | 392 | notification.__init__(self, values) 393 | self.info = sndrcvinfo(self._info) 394 | del self._info 395 | 396 | flag_DATA_UNSENT = _sctp.getconstant("SCTP_DATA_UNSENT") 397 | flag_DATA_SENT = _sctp.getconstant("SCTP_DATA_SENT") 398 | 399 | class shutdown_event(notification): 400 | """ 401 | Shutdown event. This event is received when an association goes 402 | down. The only relevant attribute is assoc_id. 403 | 404 | If user has subscribed to receive assoc_change notifications, it 405 | will receive at least 2 messages (assoc_change and shutdown_event) 406 | for an association shutdown. In addition, if the association was 407 | down by some problem, more error notifications will be received 408 | before this event. 409 | 410 | The user should never need to instantiate this directly. This 411 | notification will be received only if user subscribed to receive 412 | that (see event_subscribe class for details). 413 | """ 414 | def __init__(self, values=None): 415 | self.assoc_id = 0 416 | notification.__init__(self, values) 417 | 418 | class adaptation_event(notification): 419 | """ 420 | Adaption indication event. It signals that the remote peer requests 421 | an specific adaptation layer. 422 | 423 | In SCTP, you can pass an optional 32-bit metadata identifier 424 | along each message, called "ppid". For protocols that actively use 425 | this feature, it is like a subprotocol identifier, or "adaptation 426 | layer". The message interpreter is chosen by this value. This is 427 | mostly for telephony signaling-related protocols. 428 | 429 | When receiving this event, the new messages should be sent with 430 | the new requested ppid. 431 | 432 | For protocols that use multiple ppids, or do not take advantage of 433 | ppid at all, this notification is useless and can be ignored, or 434 | even better, unsubscribed. 435 | 436 | The user should never need to instantiate this directly. This 437 | notification will be received only if user subscribed to receive 438 | that (see event_subscribe class for details). 439 | """ 440 | def __init__(self, values=None): 441 | self.adaptation_ind = 0 442 | self.assoc_id = 0 443 | notification.__init__(self, values) 444 | 445 | class pdapi_event(notification): 446 | """ 447 | Partial delivery event. This event is received when a partial 448 | delivery event occurs. 449 | 450 | The user should never need to instantiate this directly. This 451 | notification will be received only if user subscribed to receive 452 | that (see event_subscribe class for details). 453 | 454 | The indication can be one of the indication_* values. 455 | """ 456 | def __init__(self, values=None): 457 | self.indication = 0 458 | self.assoc_id = 0 459 | notification.__init__(self, values) 460 | 461 | indication_PD_ABORTED = _sctp.getconstant("SCTP_PARTIAL_DELIVERY_ABORTED") 462 | indication_PARTIAL_DELIVERY_ABORTED = indication_PD_ABORTED 463 | 464 | #################################################### NOTIFICATION FACTORY 465 | 466 | notification_table = { 467 | notification.type_ASSOC_CHANGE: assoc_change, 468 | notification.type_PEER_ADDR_CHANGE: paddr_change, 469 | notification.type_SEND_FAILED: send_failed, 470 | notification.type_REMOTE_ERROR: remote_error, 471 | notification.type_SHUTDOWN_EVENT: shutdown_event, 472 | notification.type_PARTIAL_DELIVERY_EVENT: pdapi_event, 473 | notification.type_ADAPTATION_INDICATION: adaptation_event, 474 | } 475 | 476 | 477 | def notification_factory(raw_notification): 478 | """ 479 | This function builds a notification based on a raw dictionary 480 | received from the bottom-half (C language) of pysctp. The user 481 | should never need to call this directly. 482 | 483 | If the raw notification if of an unknown type, a "notification" 484 | superclass object is returned instead, so the user has a chance 485 | to test "type" and deal with that. A warning is sent to the 486 | stderr. It can well happen if SCTP is extended and the user is 487 | using an old version of PySCTP... 488 | 489 | If sctpsocket.unexpected_event_raises_exception (default=False) 490 | is set to True, an unknown even will raise an exception (it is 491 | not raised by this function, but it seemed important to note it 492 | here too). 493 | 494 | """ 495 | try: 496 | num_type = raw_notification["type"] 497 | except: 498 | raise ValueError("Dictionary passed as parameter has no 'type' attribute") 499 | 500 | if num_type not in notification_table: 501 | # raw object since we do not know the type of notification 502 | o = notification(raw_notification) 503 | print("Warning: an unknown notification event (value %d) has arrived" % \ 504 | num_type, file=sys.stderr) 505 | else: 506 | o = notification_table[num_type](raw_notification) 507 | return o 508 | 509 | ########### EVENT SUBSCRIBING CLASS 510 | 511 | class event_subscribe(object): 512 | """ 513 | This class implements subproperties for sctpsocket.events "property". 514 | 515 | Variables and methods: 516 | 517 | autoflush: Boolean value that indicates if properties settings should be 518 | immediately transmitted to kernel. Default = True. 519 | 520 | flush(): Flushes the properties to the kernel. If autoflush is True, it will 521 | be called every time a property is set. 522 | 523 | clear(): Sets all properties to False, except data_io that is always set to 524 | True. Guarantees that sctp_recv() will only return when an actual 525 | data message is received. 526 | 527 | When the class is insantiated, it gets the kernel default values. So, if the 528 | application just want to change one or two properties from the implementation 529 | default, it should not need to use clear(). 530 | 531 | Properties: Every property sets whether a event class will or not be received 532 | via recvmsg() or sctp_recv(). 533 | 534 | data_io: refers to sndrcvinfo() (*) 535 | association: refers to assoc_change() event 536 | address: refers to paddr_change() event 537 | send_failure: refers to send_failed() event 538 | peer_error: refers to remote_error() event 539 | shutdown: refers to shutdon_event() 540 | partial_delivery: refers to pdapi_event() 541 | adaptation_layer: refers to adaptation_event() 542 | 543 | (*) sndrcvinfo is ALWAYS returned by sctp_recv() along with message data. The 544 | data_io property just controls whether sndrcvinfo() contains useful data. 545 | 546 | Convenience aliases for properties: 547 | 548 | dataio = data_io 549 | sndrcvinfo = data_io 550 | sendfailure = send_failure 551 | peererror = peer_error 552 | partialdelivery = partial_delivery 553 | adaptationlayer = adaptation_layer 554 | """ 555 | 556 | def flush(self): 557 | """ 558 | Flushes all event properties to the kernel. 559 | """ 560 | self.container._set_events(self.__dict__) 561 | 562 | def __set_property(self, key, value): 563 | self.__dict__[key] = value 564 | if self.autoflush: 565 | self.flush() 566 | 567 | def __get_property(self, key): 568 | return self.__dict__[key] 569 | 570 | def get_adaptation_layer(self): 571 | return self.__get_property("_adaptation_layer") 572 | 573 | def get_partial_delivery(self): 574 | return self.__get_property("_partial_delivery") 575 | 576 | def get_shutdown(self): 577 | return self.__get_property("_shutdown") 578 | 579 | def get_peer_error(self): 580 | return self.__get_property("_peer_error") 581 | 582 | def get_send_failure(self): 583 | return self.__get_property("_send_failure") 584 | 585 | def get_address(self): 586 | return self.__get_property("_address") 587 | 588 | def get_association(self): 589 | return self.__get_property("_association") 590 | 591 | def get_data_io(self): 592 | return self.__get_property("_data_io") 593 | 594 | def set_adaptation_layer(self, value): 595 | self.__set_property("_adaptation_layer", value) 596 | 597 | def set_partial_delivery(self, value): 598 | self.__set_property("_partial_delivery", value) 599 | 600 | def set_shutdown(self, value): 601 | self.__set_property("_shutdown", value) 602 | 603 | def set_peer_error(self, value): 604 | self.__set_property("_peer_error", value) 605 | 606 | def set_send_failure(self, value): 607 | self.__set_property("_send_failure", value) 608 | 609 | def set_address(self, value): 610 | self.__set_property("_address", value) 611 | 612 | def set_association(self, value): 613 | self.__set_property("_association", value) 614 | 615 | def set_data_io(self, value): 616 | self.__set_property("_data_io", value) 617 | 618 | def clear(self): 619 | """ 620 | Sets all event properties do False, except data_io what is set to True. 621 | Will NOT autoflush to kernel if autoflush variable is False. 622 | """ 623 | self._data_io = 1 624 | self._association = 0 625 | self._address = 0 626 | self._send_failure = 0 627 | self._peer_error = 0 628 | self._shutdown = 0 629 | self._partial_delivery = 0 630 | self._adaptation_layer = 0 631 | 632 | if self.autoflush: 633 | self.flush() 634 | 635 | def __init__(self, container): 636 | self.autoflush = False 637 | self.clear() 638 | self.autoflush = True 639 | 640 | self.container = container 641 | 642 | self.__dict__.update(self.container._get_events()) 643 | 644 | # synonyms :) 645 | data_io = property(get_data_io, set_data_io) 646 | dataio = data_io 647 | sndrcvinfo = data_io 648 | 649 | association = property(get_association, set_association) 650 | 651 | address = property(get_address, set_address) 652 | 653 | send_failure = property(get_send_failure, set_send_failure) 654 | sendfailure = send_failure 655 | 656 | peer_error = property(get_peer_error, set_peer_error) 657 | peererror = peer_error 658 | 659 | shutdown = property(get_shutdown, set_shutdown) 660 | 661 | partial_delivery = property(get_partial_delivery, set_partial_delivery) 662 | partialdelivery = partial_delivery 663 | 664 | adaptation_layer = property(get_adaptation_layer, set_adaptation_layer) 665 | adaptationlayer = adaptation_layer 666 | 667 | 668 | ########## STRUCTURES EXCHANGED VIA set/getsockopt() 669 | 670 | class rtoinfo(object): 671 | """ 672 | Retransmission Timeout object class. This object can be read from a SCTP socket 673 | using the get_rtoinfo() method, and *written* to the socket using set_rtoinfo() 674 | socket method. 675 | 676 | Althrough the user could create a new rtoinfo() and fill it, it is better to get 677 | the current RTO info, change whatever is necessary and send it back. 678 | 679 | Relevant attributes: 680 | 681 | assoc_id: the association ID where this info came from, or where this information 682 | is going to be applied to. Ignored/unreliable for TCP-style sockets 683 | because they hold only one association. 684 | 685 | initial: Initial RTO in milisseconds 686 | 687 | max: Maximum appliable RTO in milisseconds 688 | 689 | min: Minimum appliable RTO in milisseconds. 690 | """ 691 | def __init__(self): 692 | self.assoc_id = 0 693 | self.initial = 0 694 | self.max = 0 695 | self.min = 0 696 | 697 | class assocparams(object): 698 | """ 699 | Association Parameters object class. This object can be read from a SCTP socket 700 | using the get_assocparams() socket method, and *written* to the socket using the 701 | set_assocparams() socket method. 702 | 703 | However, not all association properties are read-write. In the following list, 704 | read-write parameters are marked (rw), and read-only are marked (ro). The read-only 705 | behaviour is NOT enforced i.e. you can change an attribute in Python and it 706 | will not "stick" at socket level :) 707 | 708 | If a read-write attribute is passed as 0, it means "do not change it". 709 | 710 | FROM TSVWG document: "The maximum number of retransmissions before an address is considered 711 | unreachable is also tunable, but is address-specific, so it is covered in a separate option. 712 | If an application attempts to set the value of the association maximum retransmission 713 | parameter to more than the sum of all maximum retransmission parameters, setsockopt() 714 | shall return an error." 715 | 716 | Althrough the user could create a new object and fill it, it is better to get 717 | the current parameters object, change whatever is necessary and send it back. 718 | 719 | assoc_id: the association ID where this info came from, or where this information 720 | is going to be applied to. Ignored/unreliable for TCP-style sockets 721 | because they hold only one association. 722 | 723 | assocmaxrxt(rw): Maximum retransmission attempts to create an association 724 | 725 | number_peer_destinations(ro): Number of possible destinations a peer has 726 | 727 | peer_rwnd(ro): Peer's current receiving window, subtracted by in-flight data. 728 | 729 | local_rwnd(ro): Current local receiving window 730 | 731 | cookie_life(rw): Cookie life in miliseconds (cookies are used while creating an association) 732 | """ 733 | def __init__(self): 734 | self.assoc_id = 0 735 | self.assocmaxrxt = 0 736 | self.number_peer_destinations = 0 737 | self.peer_rwnd = 0 738 | self.local_rwnd = 0 739 | self.cookie_life = 0 740 | 741 | class paddrparams(object): 742 | """ 743 | Peer Address Parameters object class. This object can be read from a SCTP socket 744 | using the get_paddrparms() method, and *written* to the socket using the 745 | set_paddrparams() socket method. 746 | 747 | When passing this object to set peer address parameters, attributes passed as 748 | zero mean "do not change this one". This makes easier for the user to create a 749 | new object, fill and sent it, but it is easier, and better, to get the current 750 | parameters object, change whatever is necessary and send it back. 751 | 752 | assoc_id: the association ID where this info came from, or where this information 753 | is going to be applied to. Ignored/unreliable for TCP-style sockets 754 | because they hold only one association. 755 | 756 | sockaddr: the address this info came from, or where this information is going to 757 | be applied to. It *is* important even for TCP-style sockets. 758 | 759 | hbinterval: heartbeat interval in milisseconds 760 | 761 | pathmaxrxt: maximum number of retransmissions, after that this peer address 762 | is considered unreachable and another one will be tried. 763 | 764 | WARNING: THE FOLLOWING ATTRIBUTES WILL BE READ/WRITABLE ONLY IF YOUR 765 | UNDERLYING SCTP IMPLEMENTATION IS AT LEAST DRAFT 10-LEVEL. 766 | 767 | pathmtu: Path MTU while using this peer address as path 768 | 769 | sackdelay: when delayed Selective ACK is enabled, this value specifies the 770 | delay in milisseconds, for this peer address. 771 | 772 | flags: a bitmap with any subset of paddrparams.flags_* values active 773 | test with Logical AND (& operand) instead of = 774 | 775 | flags_HB_DISABLED: disable/disabled heartbeats for that peer 776 | flags_HB_ENABLED: enable/enabled heartbeats 777 | 778 | flags_SACKDELAY_*: the same song, for Selective ACK delay features 779 | 780 | flags_PMTUD_*: the same song, but for the PMTU discovery feature. 781 | 782 | For every feature, there are two different flags for ENABLE and DISABLE 783 | because this allows to pass a zeroed flag which means "do not toggle 784 | any feature". Note that *simultaneous* use of *_ENABLED and *_DISABLED 785 | for the same feature will have undefined results! 786 | 787 | 788 | """ 789 | def __init__(self): 790 | self.assoc_id = 0 791 | self.sockaddr = ("", 0) 792 | self.hbinterval = 0 793 | self.pathmaxrxt = 0 794 | self.pathmtu = 0; 795 | self.sackdelay = 0; 796 | self.flags = 0; 797 | 798 | flags_HB_DISABLED = _sctp.getconstant("SPP_HB_DISABLED") 799 | flags_HB_ENABLED = _sctp.getconstant("SPP_HB_ENABLED") 800 | flags_PMTUD_DISABLED = _sctp.getconstant("SPP_PMTUD_DISABLED") 801 | flags_PMTUD_ENABLED = _sctp.getconstant("SPP_PMTUD_ENABLED") 802 | flags_SACKDELAY_DISABLED = _sctp.getconstant("SPP_SACKDELAY_DISABLED") 803 | flags_SACKDELAY_ENABLED = _sctp.getconstant("SPP_SACKDELAY_ENABLED") 804 | 805 | # synthatical sugar 806 | flags_HB_DISABLE = flags_HB_DISABLED 807 | flags_HB_ENABLE = flags_HB_ENABLED 808 | flags_PMTUD_DISABLE = flags_PMTUD_DISABLED 809 | flags_PMTUD_ENABLE = flags_PMTUD_ENABLED 810 | flags_SACKDELAY_DISABLE = flags_SACKDELAY_DISABLED 811 | flags_SACKDELAY_ENABLE = flags_SACKDELAY_ENABLED 812 | 813 | class paddrinfo(object): 814 | """ 815 | Peer Address information object class. The user should never need to 816 | instantiate this directly. It is received via get_paddrinfo() method call. It 817 | is read-only, you cannot set anything by passing this object. 818 | 819 | Relevant attributes: 820 | 821 | assoc_id: the association ID that this object refers to. May not have an useful value 822 | if the object came from a TCP-style socket that holds only 1 association. 823 | 824 | sockaddr: address/port pair tuple that is the address being reported about 825 | 826 | state: either state_INACTIVE or state_ACTIVE 827 | 828 | cwnd: current congestion window size 829 | 830 | srtt: current estimate of round-trip time 831 | 832 | rto: current retransmission timeout 833 | 834 | mtu: current MTU 835 | """ 836 | def __init__(self): 837 | self.assoc_id = 0 # passed to getsockopt() (UDP) 838 | self.sockaddr = ("", 0) # passed to getsockopt() 839 | self.state = 0 840 | self.cwnd = 0 841 | self.srtt = 0 842 | self.rto = 0 843 | self.mtu = 0 844 | 845 | state_INACTIVE = _sctp.getconstant("SCTP_INACTIVE") 846 | state_ACTIVE = _sctp.getconstant("SCTP_ACTIVE") 847 | 848 | class status(object): 849 | """ 850 | SCTP Association Status Report object class. The user should never need to 851 | instantiate this directly. It is received via get_status() socket method call. 852 | It is read-only, you cannot set anything using this object. 853 | 854 | Relevant attributes: 855 | 856 | assoc_id: the Association ID the information refers to. Should be ignored if 857 | the object came from a TCP-style socket, which can hold only one association. 858 | 859 | state: The state of association. May be one of the status.state_* constants. 860 | 861 | WARNING: BOUND and LISTEN states are supported only on draft 10-level 862 | implementations. 863 | 864 | rwnd: Peer's current receiving window size. 865 | 866 | unackdata: unACK'ed data chuncks (not bytes) 867 | 868 | penddata: number of data chunks pending receipt 869 | 870 | primary: paddrinfo() object containing information about primary peer address. 871 | See paddrinfo docstring for specific details. 872 | 873 | instrms: number of inbound streams. (The funnyish name was copied directly from 874 | the C structure, we try to keep object attributes' names as near as possible 875 | to the C API...) 876 | 877 | outstrms: number of outbound streams 878 | 879 | fragmentation_point: "The size at which SCTP fragmentation will occur." (tsvwg) 880 | """ 881 | def __init__(self): 882 | self.assoc_id = 0 # passed by caller via getsockopt() [UDP] 883 | self.state = 0 884 | self.rwnd = 0 885 | self.unackdata = 0 886 | self.penddata = 0 887 | self.instrms = 0 888 | self.outstrms = 0 889 | self.fragmentation_point = 0 890 | self.primary = paddrinfo() 891 | 892 | state_EMPTY = _sctp.getconstant("SCTP_EMPTY") 893 | state_CLOSED = _sctp.getconstant("SCTP_CLOSED") 894 | state_COOKIE_WAIT = _sctp.getconstant("SCTP_COOKIE_WAIT") 895 | state_COOKIE_ECHOED = _sctp.getconstant("SCTP_COOKIE_ECHOED") 896 | state_ESTABLISHED = _sctp.getconstant("SCTP_ESTABLISHED") 897 | state_SHUTDOWN_PENDING = _sctp.getconstant("SCTP_SHUTDOWN_PENDING") 898 | state_SHUTDOWN_SENT = _sctp.getconstant("SCTP_SHUTDOWN_SENT") 899 | state_SHUTDOWN_RECEIVED = _sctp.getconstant("SCTP_SHUTDOWN_RECEIVED") 900 | state_SHUTDOWN_ACK_SENT = _sctp.getconstant("SCTP_SHUTDOWN_ACK_SENT") 901 | state_BOUND = _sctp.getconstant("SCTP_BOUND") 902 | state_LISTEN = _sctp.getconstant("SCTP_LISTEN") 903 | 904 | ######### IMPLEMENTATION FEATURE LIST BITMAP 905 | 906 | def features(): 907 | """ 908 | Returns a bitmap of features possessed by the SCTP underlying 909 | implementation. Individual bits can be tested against 910 | HAVE_* constants. 911 | 912 | These flags may not be completely trustable, even in the sense that 913 | some feature may be available despite absence of flag. LK-SCTP 1.0.3 has 914 | no HAVE_SCTP_PRSCTP macro, but Linux implements PR-SCTP extension 915 | since kernel 2.6.10... 916 | """ 917 | flags = HAVE_SCTP | HAVE_KERNEL_SCTP 918 | if _sctp.have_sctp_multibuf(): 919 | flags |= HAVE_SCTP_MULTIBUF 920 | if _sctp.have_sctp_noconnect(): 921 | flags |= HAVE_SCTP_NOCONNECT 922 | if _sctp.have_sctp_prsctp(): 923 | flags |= HAVE_SCTP_PRSCTP 924 | if _sctp.have_sctp_addip(): 925 | flags |= HAVE_SCTP_ADDIP 926 | if _sctp.have_sctp_setprimary(): 927 | flags |= HAVE_SCTP_CANSET_PRIMARY 928 | if _sctp.have_sctp_sat_network(): 929 | flags |= HAVE_SCTP_SAT_NETWORK_CAPABILITY 930 | return flags 931 | 932 | #################### THE REAL THING :) 933 | 934 | class sctpsocket(object): 935 | """ 936 | This is the base class for SCTP sockets. In general, the user will use sctpsocket_tcp() 937 | and sctpsocket_udp(), althrough it can use directly this class because all required 938 | functionality is, by design, implemented *here*. The subclasses are offered just for 939 | user convenience. 940 | 941 | This class does NOT inherit from python standard sockets. It CONTAINS a python socket 942 | and DELEGATES unknown method calls to that socket. So, we expect that sctpsocket 943 | objects can be used in most places where a regular socket is expected. 944 | 945 | Main methods: 946 | 947 | bindx: allows to bind to a set of network interfaces (standard bind() allows 948 | bind to exactly one or then to all). 949 | connectx: allows to connect by specifying a list of remote addresses. 950 | 951 | (For those that do not know SCTP: SCTP allows "multihomed" associations, i.e. 952 | connections over a set of peer addresses. If the primary address fails to 953 | respond (e.g. due to a network failure) it falls back to a secondary address. 954 | It is intended for automatic network redundancy.) 955 | 956 | getpaddrs: Gets the list of remote peer addresses for an association 957 | getladdrs: Gets the list of local addresses for an association 958 | sctp_send: Sends a SCTP message. Allows to pass some SCTP-specific parameters. 959 | If the specific parameters are not relevant, send() or sendto() 960 | can also be used for SCTP. 961 | sctp_recv: Receives a SCTP messages. Returns SCTP-specific metadata along 962 | with the data. If the metadata is not relevant for the 963 | application, recv()/recvfrom() and read() will also work. 964 | peeloff: Detaches ("peels off") an association from an UDP-style socket. 965 | accept: Overrides socket standard accept(), works the same way. 966 | set_peer_primary: Sets the peer primary address 967 | set_primary: Set the local primary address 968 | 969 | In addition, socket methods like bind(), connect() close(), set/getsockopt() 970 | should work as expected. We tried to implement or override just the socket 971 | methods that have special behaviour in SCTP. 972 | 973 | Delegation methods (use with care): 974 | 975 | sock(): returns the contained Python standard socket. 976 | __getattr__(): this method does the delegation magic, forwarding all 977 | unknown attribute inquiries to the python socket. 978 | 979 | Properties: 980 | 981 | nodelay: If True, disables Nagle algorithm, which causes any message to be immediately 982 | sent, potentially creating too much "tinygrams". Because of message-oriented 983 | nature of SCTP, some implementations have it turned on by default, while TCP 984 | is mandated to have it off by default. 985 | 986 | adaptation: 32-bit value related to the "ppid" metadata field sent along each data 987 | message. If this property is different than zero, the configured value 988 | is sent via ADAPTION INDICATION event to the remote peer when a new 989 | association is opened. This is intended to be used by telephony-related 990 | protocols. 991 | 992 | disable_fragments: if True, a message will never be fragmented in several datagrams. 993 | It means that message must fit in the PMTU datagram. Default is 994 | False for most implementations. 995 | 996 | mappedv4: If True, all IPv4 addresses will be received as IPv6-mapped IPv4 addresses 997 | (::ffff:0:0/96). The default is True. Otherwise, the application can receive 998 | either pure IPv6 or IPv4 addresses. 999 | 1000 | maxseg: Maximum segment size i.e. the size of a message chunk inside a datagram, 1001 | in bytes. This value indirectly limits the size of the whole datagram, since 1002 | datagram = maxseg + a fixed overhead. 1003 | 1004 | autoclose: For UDP-style sockets, sets/gets the auto-closing timeout for idle 1005 | associations, in seconds. A value of 0 means that no automatic close 1006 | will be done. This property does not work for TCP-style sockets. 1007 | 1008 | ttl: Default timetolive value to use with sctp_send. Default set to 0. 1009 | 1010 | streamid: Default SCTP stream identifier value to use with sctp_send. Default set to 0. 1011 | 1012 | IMPORTANT NOTE: the maximum message size is limited both by the implementation 1013 | and by the transmission buffer (SO_SNDBUF). SCTP applications must configure 1014 | the transmission and receiving bufers accordingly to the biggest messages it 1015 | excpects to deal with. 1016 | 1017 | """ 1018 | def __init__(self, family, style, sk): 1019 | """ 1020 | Parameters: 1021 | 1022 | family: Internet address family (socket.AF_INET or socket.AF_INET6) 1023 | 1024 | style: TCP_STYLE or UDP_STYLE. 1025 | 1026 | sk: Underlying Python socket. If None is passed, it is created. When passing an 1027 | already-created socket, be sure it matches family and style! 1028 | """ 1029 | 1030 | if not sk: 1031 | sk = socket.socket(family, style, IPPROTO_SCTP) 1032 | 1033 | self._style = style 1034 | self._sk = sk 1035 | self._family = family 1036 | self._ttl = 0 1037 | self._streamid = 0 1038 | 1039 | self.unexpected_event_raises_exception = False 1040 | self.initparams = initparams(self) 1041 | self.events = event_subscribe(self) 1042 | 1043 | self.datalogging = False 1044 | 1045 | def bindx(self, sockaddrs, action=BINDX_ADD): 1046 | """ 1047 | Binds to a list of addresses. This method() allows to bind to any subset 1048 | of available interfaces, while standard bind() allows only one specific 1049 | interface, or all of them using the INADDR_ANY pseudo-address. Also, it 1050 | allows to *remove* the binding from one or more addresses. 1051 | 1052 | If you don't need the extended functionality of bindx(), standard bind() 1053 | can be used and works as expected for SCTP. 1054 | 1055 | Parameters: 1056 | 1057 | sockaddr: List of (address, port) tuples. 1058 | action: BINDX_ADD or BINDX_REMOVE. Default is BINDX_ADD. 1059 | 1060 | bindx() raises an exception if bindx() is not successful. 1061 | """ 1062 | _sctp.bindx(self._sk.fileno(), sockaddrs, action) 1063 | 1064 | def connectx(self, sockaddrs, assoc_id=None): 1065 | """ 1066 | Connects to a remote peer. It works like standard connect(), but accepts 1067 | a list of address/port pairs, in order to support the SCTP multihoming 1068 | 1069 | Parameters: 1070 | 1071 | sockaddrs: List of (address, port) tuples. 1072 | 1073 | connectx() raises an exception if it is not successful. Warning: not all 1074 | SCTP implementations support connectx(). It will raise an RuntimeError() 1075 | if not supported. 1076 | """ 1077 | if "connectx" in _sctp.__dict__: 1078 | _sctp.connectx(self._sk.fileno(), sockaddrs, assoc_id) 1079 | else: 1080 | raise RuntimeError("Underlying SCTP implementation does not have connectx()") 1081 | 1082 | def getpaddrs(self, assoc_id = 0): # -> tuple of sockaddrs as strings 1083 | """ 1084 | Gets a list of remote address/pair tuples from an specific association. 1085 | 1086 | Parameters: 1087 | 1088 | assoc_id: Association ID of the association to be queried. If the socket is 1089 | TCP-style, this parameter is ignored. 1090 | 1091 | Returns: a list of address/port tuples. 1092 | """ 1093 | 1094 | return _sctp.getpaddrs(self._sk.fileno(), assoc_id) 1095 | 1096 | def getladdrs(self, assoc_id = 0): # -> tuple of sockaddrs as strings 1097 | """ 1098 | Gets a list of local address/pair tuples from an specific association. 1099 | 1100 | Parameters: 1101 | 1102 | assoc_id: Association ID of the association to be queried. If the socket is 1103 | TCP-style, this parameter is ignored. If zero is passed for an 1104 | UDP-style socket, it refers to the socket default local address. 1105 | 1106 | Returns: a list of address/port tuples. 1107 | 1108 | Note that if you do not bind() explicitely (e.g. in client-side programs), 1109 | this method will return only one wildcard address, which is useful only 1110 | to determinate the ephemeral port that the client is using. 1111 | """ 1112 | 1113 | return _sctp.getladdrs(self._sk.fileno(), assoc_id) 1114 | 1115 | def sctp_send(self, msg, to=("",0), ppid=None, flags=0, stream=None, timetolive=None, context=0, 1116 | record_file_prefix="RECORD_sctp_traffic", datalogging = False): 1117 | """ 1118 | Sends a SCTP message. While send()/sendto() can also be used, this method also 1119 | accepts some SCTP-exclusive metadata. Parameters: 1120 | 1121 | msg: string containing the message to be sent. 1122 | 1123 | to: an address/port tuple identifying the destination, or the assoc_id for the 1124 | association. It can (and generally must) be omitted for TCP-style sockets. 1125 | 1126 | WARNING: identifying destination by Association ID not implemented yet! 1127 | 1128 | ppid: adaptation layer value, a 32-bit metadata that is sent along the message. 1129 | Default to 0. 1130 | 1131 | flags: a bitmap of MSG_* flags. For example, MSG_UNORDERED indicates that 1132 | message can be delivered out-of-order, and MSG_EOF + empty message 1133 | shuts down the association. Defaults to no flags set. 1134 | 1135 | It does NOT include flags like MSG_DONTROUTE or other low-level flags 1136 | that are supported by sendto(). 1137 | 1138 | stream: stream number where the message will sent by. 1139 | If not set use default value. 1140 | 1141 | timetolive: time to live of the message in milisseconds. Zero means infinite 1142 | TTL. If TTL expires, the message is discarded. Discarding policy 1143 | changes whether implementation implements the PR-SCTP extension or not. 1144 | If not set use default value. 1145 | 1146 | context: an opaque 32-bit integer that will be returned in some notification events, 1147 | if the event is directly related to this message transmission. So the 1148 | application can know exactly which message triggered which event. 1149 | Defaults to 0. 1150 | 1151 | The method returns the number of bytes sent. Ideally it is going to be exacly the 1152 | size of the message. Transmission errors will trigger an exception. 1153 | 1154 | 1155 | WARNING: the maximum message size that can be sent via SCTP is limited 1156 | both by the implementation and by the transmission buffer (SO_SNDBUF). 1157 | The application must configure this buffer accordingly. 1158 | """ 1159 | 1160 | if ppid is None: 1161 | ppid = self.adaptation 1162 | 1163 | if timetolive is None: 1164 | timetolive = self._ttl 1165 | 1166 | if stream is None: 1167 | stream = self._streamid 1168 | 1169 | if datalogging == True or self.datalogging == True: 1170 | now = datetime.datetime.now() 1171 | recordfilename = record_file_prefix + "-" + now.strftime("%Y%m%d%H%M%S") + "-c2s." 1172 | i = 1 1173 | while os.path.exists(recordfilename+"%d"%i): 1174 | i = i + 1 1175 | recordlog = open(recordfilename+"%d"%i, 'w') 1176 | recordlog.write(msg) 1177 | recordlog.close() 1178 | return _sctp.sctp_send_msg(self._sk.fileno(), msg, to, ppid, flags, stream, timetolive, context) 1179 | 1180 | def sctp_recv(self, maxlen): 1181 | """ 1182 | Receives an SCTP message and/or a SCTP notification event. The notifications 1183 | that can be received are regulated by "events" property and its subproperties. 1184 | See event_subscribe() class for details. 1185 | 1186 | It is important to know that sctp_recv() can return either on data messages or 1187 | on subscribed events. If the application just wants data, it must unsubscribe 1188 | the other events. 1189 | 1190 | Parameters; 1191 | 1192 | maxlen: the maximum message size that can be received. If bigger messages are 1193 | received, they will be received in fragments (non-atomically). The application 1194 | must choose this carefully if it wants to keep the atomicity of messages! 1195 | 1196 | Returns: (fromaddr, flags, msg, notif) 1197 | 1198 | fromaddr: address/port pair. For some applications, association ID will be more 1199 | useful to identify the related association. Fortunately, assoc_id is 1200 | a attribute of most notifications received via "notif" (see below) 1201 | 1202 | flags: a bitmap of lower-level recvmsg() flags (FLAG_* flags). 1203 | 1204 | FLAG_NOTIFICATION indicates that an event notification was 1205 | returned, instead of a data message. 1206 | 1207 | FLAG_EOR indicates that this is the final fragment of a data message. 1208 | Ideally, all messages will come with this flag set. 1209 | 1210 | WARNING: message data-related flags like MSG_UNORDERED are returned 1211 | inside sndrcvinfo() notifications, and NOT here! 1212 | 1213 | msg: the actual data message. Since SCTP does not allow empty messages, 1214 | an empty "msg" always means something special, either: 1215 | 1216 | a) that "notif" contains an event notificatoin, if "flags" has 1217 | FLAG_NOTIFICATION set; 1218 | 1219 | b) that association is closing (for TCP-style sockets only) 1220 | 1221 | notif: notification event object. If "msg" is a data message, it will contain 1222 | a sndrcvinfo() object that contains metadata about the message. If 1223 | "flags" has FLAG_NOTIFICATION set, it will contain some notification() 1224 | subclass. 1225 | 1226 | sndrcvinfo() is ALWAYS returned when a data message is received, but 1227 | it will only contain useful data IF events.data_io is subscribed True. 1228 | 1229 | WARNING: the maximum message size that can be received via SCTP is limited: 1230 | 1231 | * by the underlying implementation. Check your operating system. 1232 | 1233 | * by the "maxlen" parameter passed. If message is bigger, it will be received 1234 | in several fragments. The last fragment will have FLAG_EOR flag set. 1235 | 1236 | * by the socket's reception buffer (SO_SNDRCV). The application must configure 1237 | this buffer accordingly, otherwise the message will be truncacted. 1238 | """ 1239 | (fromaddr, flags, msg, _notif) = _sctp.sctp_recv_msg(self._sk.fileno(), maxlen) 1240 | 1241 | if (flags & FLAG_NOTIFICATION): 1242 | notif = notification_factory(_notif) 1243 | if (notif.__class__ == notification): 1244 | # Raw notification class, means we do not know the exact type 1245 | # of this notification 1246 | if self.unexpected_event_raises_exception: 1247 | raise IOError("An unknown event notification has arrived") 1248 | else: 1249 | notif = sndrcvinfo(_notif) 1250 | 1251 | return (fromaddr, flags, msg, notif) 1252 | 1253 | def peeloff(self, assoc_id): 1254 | """ 1255 | Detaches ("peels off") an association from an UDP-style socket. 1256 | Will throw an IOError exception if it is not successful. 1257 | 1258 | Parameters: 1259 | 1260 | assoc_id: Association ID to be peeled off 1261 | 1262 | Returns: a sctpsocket_tcp() object. 1263 | 1264 | """ 1265 | fd = _sctp.peeloff(self._sk.fileno(), assoc_id) 1266 | if fd < 0: 1267 | raise IOError("Assoc ID does not correspond to any open association") 1268 | 1269 | sk = socket.fromfd(fd, self._family, SOCK_STREAM, IPPROTO_SCTP) 1270 | return sctpsocket_tcp(self._family, sk) 1271 | 1272 | def accept(self): 1273 | """ 1274 | Frontend for socket.accept() method. Does exactly the same thing, 1275 | except that returns an sctpsocket_tcp object instead of a standard 1276 | Python socket. May throw the same exceptions as socket.accept() 1277 | 1278 | Returns: a sctpsocket_tcp() object 1279 | """ 1280 | sk, fromaddr = self._sk.accept() 1281 | if sk: 1282 | return (sctpsocket_tcp(self._family, sk), fromaddr) 1283 | else: 1284 | raise IOError("sctpsocket.accept() failed for unknown reason") 1285 | 1286 | def set_peer_primary(self, assoc_id, addr): 1287 | """ 1288 | Requests the remote peer to use our [addr] as the primary address. Parameters: 1289 | 1290 | assoc_id: the association to be affected. Pass zero for TCP-style sockets. 1291 | 1292 | addr: address/port pair tuple. Be sure to pass a correct tuple, with the 1293 | right local port number (that can be discovered via getladdrs()). 1294 | 1295 | Raises an exception if not successful. Some implementations may not support it. 1296 | It seems to work only at server-side. 1297 | """ 1298 | 1299 | if self._style == TCP_STYLE: 1300 | if assoc_id != 0: 1301 | raise ValueError("assoc_id is ignored for TCP-style sockets, pass 0") 1302 | else: 1303 | if assoc_id == 0: 1304 | # raise ValueError("assoc_id is needed for UDP-style sockets") 1305 | # FIXME 1306 | pass 1307 | 1308 | _sctp.set_peer_primary(self._sk.fileno(), assoc_id, addr) 1309 | 1310 | def set_primary(self, assoc_id, addr): 1311 | """ 1312 | Requests the local SCTP stack to use the peer's primary address "addr". 1313 | Parameters: 1314 | 1315 | assoc_id: the association to be affected. Pass zero for TCP-style sockets. 1316 | addr: address/port pair tuple 1317 | 1318 | Raises an exception if not successful. Some implementations may not support it. 1319 | """ 1320 | 1321 | if self._style == TCP_STYLE: 1322 | if assoc_id != 0: 1323 | raise ValueError("assoc_id is ignored for TCP-style sockets, pass 0") 1324 | else: 1325 | if assoc_id == 0: 1326 | raise ValueError("assoc_id is needed for UDP-style sockets") 1327 | 1328 | _sctp.set_primary(self._sk.fileno(), assoc_id, addr) 1329 | 1330 | # Properties 1331 | 1332 | def _get_initparams(self): 1333 | """ 1334 | Private function, used only by initparams() class. Returns a dictionary 1335 | of default association init parameters for this socket (initparams property and 1336 | initparams() class that contain the subproperties). 1337 | """ 1338 | return _sctp.get_initparams(self._sk.fileno()) 1339 | 1340 | def _set_initparams(self, d): 1341 | """ 1342 | Private function, used only by initparams() class. Sets the default 1343 | association parameters for this socket (initparams property and 1344 | initparams() class that contain the subproperties). 1345 | """ 1346 | _sctp.set_initparams(self._sk.fileno(), d) 1347 | 1348 | def get_nodelay(self): 1349 | """ 1350 | Gets the status of NODELAY for the socket from the kernel. 1351 | 1352 | See class documentation for more details. (nodelay property) 1353 | """ 1354 | return _sctp.get_nodelay(self._sk.fileno()) 1355 | 1356 | def set_nodelay(self, rvalue): 1357 | """ 1358 | Sets the status of NODELAY for the socket. 1359 | 1360 | See class documentation for more details. (nodelay property) 1361 | """ 1362 | _sctp.set_nodelay(self._sk.fileno(), rvalue) 1363 | 1364 | def get_adaptation(self): 1365 | """ 1366 | Gets the adaptation layer indication from kernel. 1367 | 1368 | See class documentation for more details. (adaptation property) 1369 | """ 1370 | return _sctp.get_adaptation(self._sk.fileno()) 1371 | 1372 | def set_adaptation(self, rvalue): 1373 | """ 1374 | Sets the adaptation layer indication for this socket. 1375 | 1376 | See class documentation for more details. (adaptation property) 1377 | """ 1378 | _sctp.set_adaptation(self._sk.fileno(), rvalue) 1379 | 1380 | def get_sndbuf(self): 1381 | """ 1382 | Gets the send buffer size from the kernel for this socket. 1383 | """ 1384 | return _sctp.get_sndbuf(self._sk.fileno()) 1385 | 1386 | def set_sndbuf(self, rvalue): 1387 | """ 1388 | Sets the send buffer size in the kernel for this socket. 1389 | """ 1390 | # Linux doubles the size, hence we divise it by 2 1391 | _sctp.set_sndbuf(self._sk.fileno(), rvalue//2) 1392 | 1393 | def get_rcvbuf(self): 1394 | """ 1395 | Gets the receive buffer size from the kernel for this socket. 1396 | """ 1397 | return _sctp.get_rcvbuf(self._sk.fileno()) 1398 | 1399 | def set_rcvbuf(self, rvalue): 1400 | """ 1401 | Sets the receive buffer size in the kernel for this socket. 1402 | """ 1403 | # Linux doubles the size, hence we divise it by 2 1404 | _sctp.set_rcvbuf(self._sk.fileno(), rvalue//2) 1405 | 1406 | def get_disable_fragments(self): 1407 | """ 1408 | Queries kernel and returns True if message fragmenting is disable for 1409 | this socket. 1410 | 1411 | See class documentation for more details. (disable_fragments property) 1412 | """ 1413 | return _sctp.get_disable_fragments(self._sk.fileno()) 1414 | 1415 | def set_disable_fragments(self, rvalue): 1416 | """ 1417 | Sets whether message fragmentation should be disable for this socket. 1418 | 1419 | See class documentation for more details. (disable_fragments property) 1420 | """ 1421 | _sctp.set_disable_fragments(self._sk.fileno(), rvalue) 1422 | 1423 | def _get_events(self): 1424 | """ 1425 | Private function, used only by event_subscribe() class. Returns a dictionary 1426 | with the state of event subscriptions for this socket. 1427 | 1428 | See class documentation for more details. (events property and event_subscribe class) 1429 | """ 1430 | return _sctp.get_events(self._sk.fileno()) 1431 | 1432 | def _set_events(self, d): 1433 | """ 1434 | Private function, used only by event_subscribe() class. Sets the event 1435 | subscriptions for this socket. 1436 | 1437 | See class documentation for more details. (events property and event_subscribe class) 1438 | """ 1439 | _sctp.set_events(self._sk.fileno(), d) 1440 | 1441 | def get_mappedv4(self): 1442 | """ 1443 | Returns True if IPv4 address are be returned as IPv4-mapped IPv6 addresses 1444 | (::FFFF:0:0/96 range). 1445 | """ 1446 | return _sctp.get_mappedv4(self._sk.fileno()) 1447 | 1448 | def set_mappedv4(self, rvalue): 1449 | """ 1450 | Sets whether IPv4 address should be returned as IPv4-mapped IPv6 addresses 1451 | (::FFFF:0:0/96 range). 1452 | """ 1453 | _sctp.set_mappedv4(self._sk.fileno(), rvalue) 1454 | 1455 | def get_maxseg(self): 1456 | """ 1457 | Get the maximum segment size. i.e. the maximum size of a message chunk. 1458 | """ 1459 | return _sctp.get_maxseg(self._sk.fileno()) 1460 | 1461 | def set_maxseg(self, rvalue): 1462 | """ 1463 | Set the maximum segment size i.e. the maximum size of a message chunk. 1464 | It indirectly limits the maximum SCTP datagram size. 1465 | """ 1466 | _sctp.set_maxseg(self._sk.fileno(), rvalue) 1467 | 1468 | def get_autoclose(self): 1469 | """ 1470 | Gets the timeout value (in seconds) for idle associations, after 1471 | which they will be closed. Zero means infinite timeout. 1472 | """ 1473 | return _sctp.get_autoclose(self._sk.fileno()) 1474 | 1475 | def set_autoclose(self, rvalue): 1476 | """ 1477 | Sets the timeout of idle associations, in seconds. After that, 1478 | associations are closed. A value of zero means they are never 1479 | closed automatically. 1480 | """ 1481 | _sctp.set_autoclose(self._sk.fileno(), rvalue) 1482 | 1483 | def get_status(self, assoc_id = 0): 1484 | """ 1485 | Returns a status structure for an SCTP association. For more information 1486 | about the returned data, see status() class docstring. 1487 | 1488 | Parameters: 1489 | 1490 | assoc_id: the association ID of the association. Must be zero or not passed at all 1491 | for TCP-style sockets. 1492 | 1493 | """ 1494 | if self._style == TCP_STYLE: 1495 | if assoc_id != 0: 1496 | raise ValueError("assoc_id is ignored for TCP-style sockets, pass 0") 1497 | else: 1498 | if assoc_id == 0: 1499 | raise ValueError("assoc_id is needed for UDP-style sockets") 1500 | 1501 | s = status() 1502 | s.assoc_id = assoc_id 1503 | _sctp.get_status(self._sk.fileno(), s.__dict__, s.primary.__dict__) 1504 | 1505 | return s 1506 | 1507 | def get_paddrinfo(self, assoc_id, sockaddr): 1508 | """ 1509 | Returns a paddrinfo() object relative to an association/peer address pair. 1510 | For more information about the returned data, see paddrinfo() class docstring. 1511 | 1512 | Parameters: 1513 | 1514 | assoc_id: the association ID of the association. Must be zero for TCP-style sockets. 1515 | 1516 | sockaddr: the address/port tuple. Due to the nature of the information, it can not 1517 | be a wildcard (there is no "association-wide" information here); a concrete 1518 | peer address must be passed. 1519 | 1520 | This method distinguishes from get_paddrparams() because information returned here 1521 | is read-only -- there is NOT a set_paddrinfo() method. 1522 | 1523 | A paddrinfo() object related to the primary association address is indirectly 1524 | returned by get_status(), in the status.primary attribute. 1525 | """ 1526 | if self._style == TCP_STYLE: 1527 | if assoc_id != 0: 1528 | raise ValueError("assoc_id is ignored for TCP-style sockets, pass 0") 1529 | else: 1530 | if assoc_id == 0: 1531 | raise ValueError("assoc_id is needed for UDP-style sockets") 1532 | 1533 | s = paddrinfo() 1534 | s.assoc_id = assoc_id 1535 | s.sockaddr = sockaddr 1536 | _sctp.get_paddrinfo(self._sk.fileno(), s.__dict__) 1537 | 1538 | return s 1539 | 1540 | def get_assocparams(self, assoc_id = 0): 1541 | """ 1542 | Returns a assocparams() object relative to a SCTP association. For more information 1543 | about the returned data, see assocparams() class docstring. 1544 | 1545 | Parameters: 1546 | 1547 | assoc_id: the association ID of the association. Must be zero or not passed at all 1548 | for TCP-style sockets. If you pass a zero for UDP-style sockets, it will 1549 | return the default parameters of the whole endpoint. 1550 | """ 1551 | 1552 | s = assocparams() 1553 | s.assoc_id = assoc_id 1554 | _sctp.get_assocparams(self._sk.fileno(), s.__dict__) 1555 | 1556 | return s 1557 | 1558 | def set_assocparams(self, o): 1559 | """ 1560 | Sets parameters for a SCTP association. Parameters: 1561 | 1562 | o: assocparams() object containing the assoc_id of the association be 1563 | affected, plus the association parameters. If assoc_id is zero and 1564 | socket is UDP style, it will set the default parameters for future 1565 | associations. 1566 | 1567 | Warning: it seems not to work in TCP-style sockets, in client side. 1568 | (we do not know the reason.) 1569 | 1570 | It is advisable not to create the assocparams() object from scratch, but 1571 | rather get it with assocparms(), change whatever necessary and send it back. 1572 | It guarantees that it has sensible defaults and a correct identification of 1573 | the association to be affected. 1574 | """ 1575 | _sctp.set_assocparams(self._sk.fileno(), o.__dict__) 1576 | 1577 | def get_paddrparams(self, assoc_id, sockaddr): 1578 | """ 1579 | Returns a paddrparams() object relative to an association/peer address pair. 1580 | For more information about the returned data, see paddrparams() class docstring. 1581 | 1582 | Parameters: 1583 | 1584 | assoc_id: the association ID of the association. Must be zero for TCP-style sockets. 1585 | 1586 | sockaddr: the address/port tuple. Due to the nature of the information, it can not 1587 | be a wildcard (there is no "association-wide" information here); a concrete 1588 | peer address must be passed. 1589 | 1590 | Note: For TCP-style, client-side sockets, only the wildcard address 1591 | ("",0) seems to work. We don't know exactly why :) 1592 | 1593 | This method distinguishes from get_paddrinfo() because information returned here 1594 | can be changed at socket level, while *paddrinfo() is read-only. 1595 | """ 1596 | if self._style == TCP_STYLE: 1597 | if assoc_id != 0: 1598 | raise ValueError("assoc_id is ignored for TCP-style sockets, pass 0") 1599 | else: 1600 | if assoc_id == 0: 1601 | raise ValueError("assoc_id is needed for UDP-style sockets") 1602 | 1603 | s = paddrparams() 1604 | s.assoc_id = assoc_id 1605 | s.sockaddr = sockaddr 1606 | _sctp.get_paddrparams(self._sk.fileno(), s.__dict__) 1607 | 1608 | return s 1609 | 1610 | def set_paddrparams(self, o): 1611 | """ 1612 | Sets peer address parameters for a SCTP association. Parameters: 1613 | 1614 | o: paddrparams() object containing the assoc_id/address of the peer to be 1615 | affected, plus the address parameters. 1616 | 1617 | It is advisable not to create the paddrparams() object from scratch, but 1618 | rather get it with get_paddrparms(), change whatever necessary and send it back. 1619 | It guarantees that it has sensible defaults and a correct identification of 1620 | the association/address to be affected. 1621 | """ 1622 | _sctp.set_paddrparams(self._sk.fileno(), o.__dict__) 1623 | 1624 | def get_rtoinfo(self, assoc_id = 0): 1625 | """ 1626 | Returns a RTO information structure for a SCTP association. For more information 1627 | about the returned data, see rtoinfo() class docstring. 1628 | 1629 | Parameters: 1630 | 1631 | assoc_id: the association ID of the association. Must be zero or not passed at all 1632 | for TCP-style sockets. If zero is passed for UDP-style sockets, the 1633 | information refers to the socket defaults, and not any particular 1634 | opened association. 1635 | """ 1636 | 1637 | s = rtoinfo() 1638 | s.assoc_id = assoc_id 1639 | _sctp.get_rtoinfo(self._sk.fileno(), s.__dict__) 1640 | 1641 | return s 1642 | 1643 | def set_rtoinfo(self, o): 1644 | """ 1645 | Sets RTO parameters for a SCTP association. Parameters: 1646 | 1647 | o: rtoinfo() object containing the assoc_id of the association to be 1648 | affected, plus the RTO parameters. If an assoc_id of zero is passed 1649 | for UDP-style socket, the information will affect the socket defaults, 1650 | not any particular association. 1651 | 1652 | It is advisable not to create rtoinfo() from scratch, but rather get it 1653 | from get_rtoinfo(), change whatever necessary and send it back. 1654 | """ 1655 | _sctp.set_rtoinfo(self._sk.fileno(), o.__dict__) 1656 | 1657 | def get_ttl(self): 1658 | """ 1659 | Read default time to live value, 0 mean infinite 1660 | """ 1661 | return self._ttl 1662 | 1663 | def set_ttl(self, newVal): 1664 | """ 1665 | Write default time to live 1666 | """ 1667 | if not isinstance(newVal, int) or newVal < 0 or newVal > 255: 1668 | raise ValueError('TTL shall be >= 0 and <= 255') 1669 | 1670 | self._ttl = newVal 1671 | 1672 | def get_streamid(self): 1673 | """ 1674 | Read default stream identifier 1675 | """ 1676 | return self._streamid 1677 | 1678 | def set_streamid(self, newVal): 1679 | """ 1680 | Write default stream identifier 1681 | """ 1682 | if not isinstance(newVal, int) or newVal < 0 or newVal > 65535: 1683 | raise ValueError('streamid shall be a valid unsigned 16bits integer') 1684 | 1685 | self._streamid = newVal 1686 | 1687 | # delegation 1688 | 1689 | def sock(self): 1690 | """ 1691 | Returns the underlying Python socket of an SCTP socket object. Use with care. 1692 | """ 1693 | return self._sk 1694 | 1695 | def __getattr__(self, name): 1696 | """ 1697 | Delegation trick that routes every unknown attribute to the underlying 1698 | Python socket (self._sk). This will allow a sctpsocket() to be used in most 1699 | places where a standard socket or file is expected. 1700 | """ 1701 | return getattr(self._sk, name) 1702 | 1703 | # properties 1704 | 1705 | nodelay = property(get_nodelay, set_nodelay) 1706 | adaptation = property(get_adaptation, set_adaptation) 1707 | disable_fragments = property(get_disable_fragments, set_disable_fragments) 1708 | mappedv4 = property(get_mappedv4, set_mappedv4) 1709 | maxseg = property(get_maxseg, set_maxseg) 1710 | autoclose = property(get_autoclose, set_autoclose) 1711 | ttl = property(get_ttl, set_ttl) 1712 | streamid = property(get_streamid, set_streamid) 1713 | 1714 | class sctpsocket_tcp(sctpsocket): 1715 | """ 1716 | This class represents TCP-style SCTP sockets. Its objects can be used 1717 | in most places where a standard Python socket is accepted. 1718 | 1719 | TCP-style sockets hold at most one association per socket, much like 1720 | TCP sockets, hence the name. 1721 | 1722 | This class overrides peeloff() and makes it raise an exception because 1723 | it is not valid for TCP-style sockets.The get/set methods for autoclose 1724 | property are also disabled that way. 1725 | 1726 | getpaddrs() and getladdrs() methods are overriden so they can be called 1727 | without the assoc_id parameter (since the association is implicit for 1728 | this type of socket). Still, if a parameter is passed, it is simply 1729 | ignored, to avoid creating unnecessary interface differences between 1730 | TCP and UDP-style sockets. 1731 | 1732 | set_primary() and set_peer_primary() are NOT overriden in order 1733 | to omit the assoc_id, so be sure to pass a (zero) assoc_id. 1734 | 1735 | All functionalities are inherited from the sctpsocket() father class. 1736 | This class is just a front-end. For more documentation, take a look 1737 | at sctpsocket() class, as well as documentation of the methods. 1738 | """ 1739 | def __init__(self, family, sk=None): 1740 | """ 1741 | Creates a TCP-style SCTP socket. Parameters: 1742 | 1743 | family: socket.AF_INET or socket.AF_INET6 1744 | 1745 | sk: a Python socket, if it already exists. If not passed, a new 1746 | socket is automatically created. This is useful if a SCTP socket 1747 | is received as standard input/output and the application just 1748 | wants to "repackage" it as a sctpsocket_tcp() 1749 | """ 1750 | sctpsocket.__init__(self, family, TCP_STYLE, sk) 1751 | 1752 | def peeloff(self, *params): 1753 | """ 1754 | Stub method to block unappropriate calling. 1755 | """ 1756 | raise IOError("TCP-style sockets have no peeloff operation") 1757 | 1758 | def get_autoclose(self): 1759 | """ 1760 | Stub method to block unappropriate calling. 1761 | """ 1762 | raise IOError("TCP-style sockets do not support AUTOCLOSE") 1763 | 1764 | def set_autoclose(self, rvalue): 1765 | """ 1766 | Stub method to block unappropriate calling. 1767 | """ 1768 | raise IOError("TCP-style sockets do not support AUTOCLOSE") 1769 | 1770 | class sctpsocket_udp(sctpsocket): 1771 | """ 1772 | This class represents UDP-style SCTP sockets. Its objects can be used 1773 | in most places where a standard Python socket is accepted. 1774 | 1775 | An UDP-style SCTP socket can hold several associations via a single 1776 | socket. In addition, associations will be opened automatically when a 1777 | message is sent (via standard sendto() or SCTP-only sctp_send()). It 1778 | resembles sockets communication via UDP, hence the name. 1779 | 1780 | It is opportune to remember that listen() must be called for UDP-style 1781 | sockets too, because SCTP is, after all, a connection-oriented protocol. 1782 | 1783 | The associations are also closed automatically after a idle time. The 1784 | timeout is acessible/configurable via sctpsocket.autoclose property. 1785 | The value is in seconds, 120 is a common default. Setting it to zero 1786 | means that associations are never (automatically) closed. 1787 | 1788 | Associations can be opened and/or closed manually by passing MSG_EOF flag 1789 | along an empty message (empty messages are not actually allowed by SCTP). 1790 | 1791 | All functionalities are inherited from the sctpsocket() father class. 1792 | This class is just a front-end. For more documentation, take a look 1793 | at sctpsocket() class, as well as documentation of the methods. 1794 | """ 1795 | def __init__(self, family, sk=None): 1796 | """ 1797 | Creates a sctpsocket_udp() object. Parameters: 1798 | 1799 | family: socket.AF_INET or socket.AF_INET6. 1800 | 1801 | sk (optional): Normally. sctpsocket() creates its own socket. 1802 | if "sk" is passed, it will use the passed socket instead. Make 1803 | sure it matches address family, protocol (SCTP) and style. In 1804 | general, users should never need to pass this parameter. 1805 | """ 1806 | sctpsocket.__init__(self, family, UDP_STYLE, sk) 1807 | 1808 | def accept(self, *params): 1809 | """ 1810 | Stub method to block unappropriate calling. 1811 | """ 1812 | raise IOError("UDP-style sockets have no accept() operation") 1813 | 1814 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | """ 2 | setup.py 3 | 4 | Created by Philippe Langlois on 2009-11-02. 5 | Copyright (c) 2009 Philippe Langlois. All rights reserved. 6 | 7 | This library is free software; you can redistribute it and/or modify it 8 | under the terms of the GNU Lesser General Public License as published by the 9 | Free Software Foundation; either version 2.1 of the License, or (at your 10 | option) any later version. 11 | 12 | This library is distributed in the hope that it will be useful, but WITHOUT 13 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 15 | details. 16 | 17 | You should have received a copy of the GNU Lesser General Public License 18 | along with this library; If not, see . 19 | 20 | """ 21 | 22 | import setuptools 23 | from distutils.core import setup, Extension 24 | 25 | setup(name='pysctp', 26 | version='0.7.2', 27 | license = "LGPL", 28 | description = 'pysctp is a python module for the SCTP protocol stack and library', 29 | long_description = 'pysctp is a python wrapper for the SCTP protocol stack and library. On Mac OS X you will need the SCTP NKE (Kernel Extensions). On Linux systems, you need an SCTP-aware kernel (most are) and install the following packages (debian): apt install libsctp-dev libsctp1 lksctp-tools', 30 | url = "https://github.com/p1sec/pysctp", 31 | keywords = "SCTP SIGTRAN", 32 | platforms = ["Linux", "Debian", "Ubuntu", "Fedora", "Mac OS X (untested)"], 33 | classifiers = ['Development Status :: 5 - Production/Stable', 34 | 'Intended Audience :: Developers', 35 | 'License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)', 36 | 'Programming Language :: C', 37 | 'Programming Language :: Python', 38 | 'Topic :: Software Development :: Libraries :: Python Modules', 39 | 'Topic :: System :: Networking' ], 40 | py_modules=['sctp'], 41 | ext_modules=[Extension('_sctp', sources=['_sctp.c'], 42 | include_dirs=['.', '/usr/include'], 43 | libraries=['sctp'], 44 | library_dirs=['/usr/lib/', '/usr/local/lib/'], 45 | ) 46 | ], 47 | data_files=[('include', ['_sctp.h'])], 48 | author='Elvis Pfutzenreuter', 49 | author_email='epx@epx.com.br', 50 | maintainer='Benoit Michau', 51 | maintainer_email='benoit.michau@p1sec.com', 52 | ) 53 | -------------------------------------------------------------------------------- /test_local_cnx.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # 5 | # Copyright (C) 2009 Philippe Langlois - all rights reserved 6 | # 7 | # Test with sctp_test from lksctp: 8 | # sctp_test -H 127.0.0.1 -P 10000 -l 9 | # 10 | # Test with sctpscan from P1 Security / Philippe Langlois: 11 | # sctpscan -d 12 | # 13 | # 14 | # This library is free software; you can redistribute it and/or modify it 15 | # under the terms of the GNU Lesser General Public License as published by the 16 | # Free Software Foundation; either version 2.1 of the License, or (at your 17 | # option) any later version. 18 | # 19 | # This library is distributed in the hope that it will be useful, but WITHOUT 20 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 21 | # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 22 | # details. 23 | # 24 | # You should have received a copy of the GNU Lesser General Public License 25 | # along with this library; If not, see . 26 | 27 | """ 28 | Test with sctp_test from lksctp: 29 | sctp_test -H 127.0.0.1 -P 10000 -l 30 | 31 | Then run: 32 | python ./test_local_cnx.py 33 | or 34 | python3 ./test_local_cnx.py 35 | """ 36 | 37 | import _sctp 38 | import sctp 39 | from sctp import * 40 | import time 41 | 42 | client = "127.0.0.1" 43 | server = "127.0.0.1" 44 | tcpport = 10000 45 | 46 | if _sctp.getconstant("IPPROTO_SCTP") != 132: 47 | raise(Exception("getconstant failed")) 48 | tcp = sctpsocket_tcp(socket.AF_INET) 49 | 50 | saddr = (server, tcpport) 51 | 52 | print("TCP %r ----------------------------------------------" % (saddr, )) 53 | 54 | tcp.initparams.max_instreams = 3 55 | tcp.initparams.num_ostreams = 3 56 | 57 | tcp.events.clear() 58 | tcp.events.data_io = 1 59 | 60 | tcp.connect(saddr) 61 | 62 | tcp.sctp_send(b"ABCDEF: TEST SUCCEEDED (test_local_cnx.py (C) 2009 Philippe Langlois)\n\l") 63 | while 1: 64 | fromaddr, flags, msgret, notif = tcp.sctp_recv(1000) 65 | print(" Msg arrived, flag %d" % flags) 66 | 67 | if flags & FLAG_NOTIFICATION: 68 | raise(Exception("We did not subscribe to receive notifications!")) 69 | #else: 70 | print("%s" % msgret) 71 | 72 | tcp.close() 73 | -------------------------------------------------------------------------------- /test_loopback.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # 5 | # Copyright (C) 2009 Philippe Langlois - all rights reserved 6 | # 7 | # Test with sctp_test from lksctp: 8 | # sctp_test -H 127.0.0.1 -P 10000 -l 9 | # 10 | # Test with sctpscan from P1 Security / Philippe Langlois: 11 | # sctpscan -d 12 | # 13 | # 14 | # This library is free software; you can redistribute it and/or modify it 15 | # under the terms of the GNU Lesser General Public License as published by the 16 | # Free Software Foundation; either version 2.1 of the License, or (at your 17 | # option) any later version. 18 | # 19 | # This library is distributed in the hope that it will be useful, but WITHOUT 20 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 21 | # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 22 | # details. 23 | # 24 | # You should have received a copy of the GNU Lesser General Public License 25 | # along with this library; If not, see . 26 | 27 | import sys 28 | import time 29 | import socket 30 | import _sctp 31 | import sctp 32 | 33 | 34 | addr_client = ("127.0.0.1", 10002) 35 | addr_server = ("127.0.0.1", 10001) 36 | 37 | if _sctp.getconstant("IPPROTO_SCTP") != 132: 38 | raise(Exception("getconstant failed")) 39 | 40 | def init_server(): 41 | srv = sctp.sctpsocket_tcp(socket.AF_INET) 42 | srv.bind(addr_server) 43 | srv.listen(5) 44 | return srv 45 | 46 | def test_cli(): 47 | srv = init_server() 48 | # 49 | cli = sctp.sctpsocket_tcp(socket.AF_INET) 50 | # config SCTP number of streams 51 | cli.initparams.max_instreams = 3 52 | cli.initparams.num_ostreams = 3 53 | # disable SCTP events 54 | cli.events.clear() 55 | cli.events.data_io = 1 56 | # 57 | cli.bind(addr_client) 58 | cli.connect(addr_server) 59 | # 60 | buf = b"ABCDEF: TEST SUCCEEDED (test_local_cnx.py (C) 2009 Philippe Langlois)\n\l" 61 | cli.sctp_send(buf) 62 | print("client sctp_send: %s" % buf) 63 | print("") 64 | # 65 | time.sleep(0.01) 66 | # 67 | srv_to_cli, _addr_client = srv.accept() 68 | fromaddr, flags, msgret, notif = srv_to_cli.sctp_recv(2048) 69 | print("server sctp_recv, flag %d" % flags) 70 | print("server sctp_recv, buf: %s" % msgret) 71 | print("") 72 | # 73 | cli.close() 74 | time.sleep(0.01) 75 | srv.close() 76 | # 77 | return 0 78 | 79 | if __name__ == '__main__': 80 | sys.exit(test_cli()) 81 | 82 | -------------------------------------------------------------------------------- /test_remote_cnx.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # 5 | # Copyright (C) 2009 Philippe Langlois - all rights reserved 6 | # 7 | # Test with sctp_test from lksctp: 8 | # sctp_test -H 127.0.0.1 -P 10000 -l 9 | # 10 | # Test with sctpscan from P1 Security / Philippe Langlois: 11 | # sctpscan -d 12 | # (see http://www.p1sec.com/corp/research/tools/sctpscan/ ) 13 | # 14 | # (Works only with Python >= 2.3 because of OptionParser usage) 15 | # 16 | # This library is free software; you can redistribute it and/or modify it 17 | # under the terms of the GNU Lesser General Public License as published by the 18 | # Free Software Foundation; either version 2.1 of the License, or (at your 19 | # option) any later version. 20 | # 21 | # This library is distributed in the hope that it will be useful, but WITHOUT 22 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 23 | # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 24 | # details. 25 | # 26 | # You should have received a copy of the GNU Lesser General Public License 27 | # along with this library; If not, see . 28 | 29 | import time 30 | import _sctp 31 | import sctp 32 | from sctp import * 33 | from optparse import OptionParser 34 | 35 | parser = OptionParser() 36 | parser.add_option("-H", "--host", dest="server", 37 | help="connect to HOST", action = "store", metavar="HOST", default="10.37.129.140") 38 | # parser.add_option("-f", "--file", dest="filename", 39 | # help="write report to FILE", metavar="FILE") 40 | parser.add_option("-P", "--port", dest="tcpport", 41 | help="connect to PORT", action = "store", metavar="PORT", type="int", default=10000) 42 | # 43 | parser.add_option("-p", "--localport", dest="localport", 44 | help="connect from local PORT", action = "store", metavar="PORT", type="int", default=0) 45 | parser.add_option("-q", "--quiet", 46 | action="store_false", dest="verbose", default=True, 47 | help="don't print status messages to stdout") 48 | 49 | (options, args) = parser.parse_args() 50 | 51 | # client = "127.0.0.1" 52 | # server = "10.37.129.140" 53 | server = options.server 54 | # tcpport = 10000 55 | tcpport = options.tcpport 56 | 57 | if _sctp.getconstant("IPPROTO_SCTP") != 132: 58 | raise "getconstant failed" 59 | tcp = sctpsocket_tcp(socket.AF_INET) 60 | 61 | saddr = (server, tcpport) 62 | 63 | print("TCP %r ----------------------------------------------" % (saddr, )) 64 | 65 | tcp.initparams.max_instreams = 3 66 | tcp.initparams.num_ostreams = 3 67 | 68 | tcp.events.clear() 69 | tcp.events.data_io = 1 70 | 71 | if options.localport != 0: 72 | # tcp.bindx([("", options.localport)]) 73 | print("Binding...") 74 | tcp.bind(("", options.localport)) 75 | 76 | tcp.connect(saddr) 77 | 78 | tcp.sctp_send(b"ABCDEF: TEST SUCCEEDED (test_local_cnx.py (C) 2009 Philippe Langlois)\n\l") 79 | while 1: 80 | fromaddr, flags, msgret, notif = tcp.sctp_recv(1000) 81 | print(" Msg arrived, flag %d" % flags) 82 | 83 | if flags & FLAG_NOTIFICATION: 84 | raise(Exception("We did not subscribe to receive notifications!")) 85 | # else: 86 | print("%s" % msgret) 87 | 88 | tcp.close() 89 | --------------------------------------------------------------------------------