This is a demo that performs SSL tunneling (client and/or server) and is
authorGeoff Thorpe <geoff@openssl.org>
Wed, 1 Nov 2000 23:11:19 +0000 (23:11 +0000)
committerGeoff Thorpe <geoff@openssl.org>
Wed, 1 Nov 2000 23:11:19 +0000 (23:11 +0000)
built using an abstracted state machine with a non-blocking IP wrapper
around it. README will follow in the next commit.

demos/tunala/A-client.pem [new file with mode: 0644]
demos/tunala/A-server.pem [new file with mode: 0644]
demos/tunala/CA.pem [new file with mode: 0644]
demos/tunala/Makefile [new file with mode: 0644]
demos/tunala/buffer.c [new file with mode: 0644]
demos/tunala/ip.c [new file with mode: 0644]
demos/tunala/sm.c [new file with mode: 0644]
demos/tunala/tunala.c [new file with mode: 0644]
demos/tunala/tunala.h [new file with mode: 0644]

diff --git a/demos/tunala/A-client.pem b/demos/tunala/A-client.pem
new file mode 100644 (file)
index 0000000..6b6c341
--- /dev/null
@@ -0,0 +1,85 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 1 (0x1)
+        Signature Algorithm: md5WithRSAEncryption
+        Issuer: C=NZ, L=Wellington, O=Really Irresponsible Authorisation Authority (RIAA), OU=Cert-stamping, CN=Jackov al-Trades/Email=none@fake.domain
+        Validity
+            Not Before: Nov  1 21:09:30 2000 GMT
+            Not After : Nov  1 21:09:30 2001 GMT
+        Subject: C=NZ, L=Auckland, O=Silly City-Boys Corp., OU=Internal counselling, CN=tunala-client/Email=client@fake.domain
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+            RSA Public Key: (1024 bit)
+                Modulus (1024 bit):
+                    00:a5:b2:8e:de:05:d1:fe:29:f7:46:a6:6b:a0:e5:
+                    41:2c:f7:55:a8:53:5a:e8:37:1a:68:29:5d:fd:7e:
+                    a4:40:b5:ce:e0:44:54:9e:c6:f8:9a:40:bb:45:41:
+                    57:cc:dc:97:d9:66:3e:68:9d:29:93:15:f9:2b:75:
+                    aa:49:7f:e6:cf:7f:95:44:8e:41:ed:05:97:1e:cb:
+                    3f:03:33:b0:21:2b:89:67:08:cc:7b:b2:ef:fa:26:
+                    c7:ea:21:f2:77:5f:bd:e2:67:36:0a:e2:3d:ee:76:
+                    4f:d2:d3:e9:bb:22:c0:bb:0c:e4:a2:8c:aa:71:3b:
+                    75:6c:46:a7:d2:1b:57:ee:f3
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            Netscape Comment: 
+                OpenSSL Generated Certificate
+            X509v3 Subject Key Identifier: 
+                74:A8:8B:1F:5B:59:55:09:CB:98:5C:2F:41:87:CA:AC:D4:B5:04:FF
+            X509v3 Authority Key Identifier: 
+                keyid:57:5A:5D:E0:71:0B:0C:E0:0F:51:87:DE:08:C8:25:E7:F7:87:46:C0
+                DirName:/C=NZ/L=Wellington/O=Really Irresponsible Authorisation Authority (RIAA)/OU=Cert-stamping/CN=Jackov al-Trades/Email=none@fake.domain
+                serial:00
+
+    Signature Algorithm: md5WithRSAEncryption
+        5f:36:74:77:1d:c7:56:71:d3:32:30:12:8f:4d:8e:25:90:1c:
+        b3:2e:e0:c1:77:75:ea:2c:aa:ef:82:c8:e0:86:8c:df:dc:e1:
+        86:68:81:91:1e:4e:56:b1:5d:93:6f:80:10:80:c3:d8:c4:3b:
+        0f:20:dd:43:b5:f8:09:a4:bb:a9:04:11:e0:d4:f9:d9:83:19:
+        2a:da:7c:bb:4c:c2:4f:57:43:0d:1a:1e:31:61:fb:24:62:92:
+        74:12:9c:51:ef:12:3e:23:e4:45:66:2b:ac:00:60:bf:a0:ff:
+        43:da:95:c3:cf:64:40:d2:97:8d:c1:dd:26:ce:5f:b9:a1:ea:
+        92:32
+-----BEGIN CERTIFICATE-----
+MIIECDCCA3GgAwIBAgIBATANBgkqhkiG9w0BAQQFADCBtDELMAkGA1UEBhMCTlox
+EzARBgNVBAcTCldlbGxpbmd0b24xPDA6BgNVBAoTM1JlYWxseSBJcnJlc3BvbnNp
+YmxlIEF1dGhvcmlzYXRpb24gQXV0aG9yaXR5IChSSUFBKTEWMBQGA1UECxMNQ2Vy
+dC1zdGFtcGluZzEZMBcGA1UEAxMQSmFja292IGFsLVRyYWRlczEfMB0GCSqGSIb3
+DQEJARYQbm9uZUBmYWtlLmRvbWFpbjAeFw0wMDExMDEyMTA5MzBaFw0wMTExMDEy
+MTA5MzBaMIGaMQswCQYDVQQGEwJOWjERMA8GA1UEBxMIQXVja2xhbmQxHjAcBgNV
+BAoTFVNpbGx5IENpdHktQm95cyBDb3JwLjEdMBsGA1UECxMUSW50ZXJuYWwgY291
+bnNlbGxpbmcxFjAUBgNVBAMTDXR1bmFsYS1jbGllbnQxITAfBgkqhkiG9w0BCQEW
+EmNsaWVudEBmYWtlLmRvbWFpbjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA
+pbKO3gXR/in3RqZroOVBLPdVqFNa6DcaaCld/X6kQLXO4ERUnsb4mkC7RUFXzNyX
+2WY+aJ0pkxX5K3WqSX/mz3+VRI5B7QWXHss/AzOwISuJZwjMe7Lv+ibH6iHyd1+9
+4mc2CuI97nZP0tPpuyLAuwzkooyqcTt1bEan0htX7vMCAwEAAaOCAUAwggE8MAkG
+A1UdEwQCMAAwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRp
+ZmljYXRlMB0GA1UdDgQWBBR0qIsfW1lVCcuYXC9Bh8qs1LUE/zCB4QYDVR0jBIHZ
+MIHWgBRXWl3gcQsM4A9Rh94IyCXn94dGwKGBuqSBtzCBtDELMAkGA1UEBhMCTlox
+EzARBgNVBAcTCldlbGxpbmd0b24xPDA6BgNVBAoTM1JlYWxseSBJcnJlc3BvbnNp
+YmxlIEF1dGhvcmlzYXRpb24gQXV0aG9yaXR5IChSSUFBKTEWMBQGA1UECxMNQ2Vy
+dC1zdGFtcGluZzEZMBcGA1UEAxMQSmFja292IGFsLVRyYWRlczEfMB0GCSqGSIb3
+DQEJARYQbm9uZUBmYWtlLmRvbWFpboIBADANBgkqhkiG9w0BAQQFAAOBgQBfNnR3
+HcdWcdMyMBKPTY4lkByzLuDBd3XqLKrvgsjghozf3OGGaIGRHk5WsV2Tb4AQgMPY
+xDsPIN1DtfgJpLupBBHg1PnZgxkq2ny7TMJPV0MNGh4xYfskYpJ0EpxR7xI+I+RF
+ZiusAGC/oP9D2pXDz2RA0peNwd0mzl+5oeqSMg==
+-----END CERTIFICATE-----
+
+-----BEGIN RSA PRIVATE KEY-----
+MIICWwIBAAKBgQClso7eBdH+KfdGpmug5UEs91WoU1roNxpoKV39fqRAtc7gRFSe
+xviaQLtFQVfM3JfZZj5onSmTFfkrdapJf+bPf5VEjkHtBZceyz8DM7AhK4lnCMx7
+su/6JsfqIfJ3X73iZzYK4j3udk/S0+m7IsC7DOSijKpxO3VsRqfSG1fu8wIDAQAB
+AoGAUWBCHfQJz6NYl8//p8MvoR/PZ52YVddKFsHKMUbS8BzZ/vZQALKDQW5tCnQu
+5KOpx9EY8VPOKThvaNKe0P4Joi+ozOm9UHABAQAL/dzk6NP4NG/GYoBdYDhRowbo
+3mdxgbJ95INznK3WhGZNGnhd4umz0P67U2xyc5PklKqCGuECQQDcZQE0ZTi1hQ7Z
+hStH5h0soyz9VQrx9wF5nfC64lKbh0f6k/pRJqanJ9EDE9od1tuxyWi0IGhOWGC0
+LPeB3S+LAkEAwHdptzi+c4hsGqoAEIuYvXah02LOYS2nCpcOssy3jwj8u6IWqiDf
+KGt8J3S5u1afRIca9wgJiFME754FYdQrOQJACerNnBL1800Sdv1EDk8vfuO00Y1z
+GaI4wcU2oOIwP2ld9suT1vT7SMhE4nORPAiACEb83CYdK3FUoKbpWEGgswJAe8aP
++yAIAz7x3vgDUKCmGvqHOe2qMf7tFTduYXicd+Vcu8KS9thrC5CMaMd9hsg/Zw/I
+PKQzlTNm0j760R5kqQJAeO239gMrscKDFvsqSYr3UKf7uJxCetikhVZE1LH//WiM
+Rl5jhu6fL9FgNWuyM0z8VTIgZAX6RwZdn1fb3mFYjw==
+-----END RSA PRIVATE KEY-----
diff --git a/demos/tunala/A-server.pem b/demos/tunala/A-server.pem
new file mode 100644 (file)
index 0000000..a35661b
--- /dev/null
@@ -0,0 +1,85 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 2 (0x2)
+        Signature Algorithm: md5WithRSAEncryption
+        Issuer: C=NZ, L=Wellington, O=Really Irresponsible Authorisation Authority (RIAA), OU=Cert-stamping, CN=Jackov al-Trades/Email=none@fake.domain
+        Validity
+            Not Before: Nov  1 21:14:03 2000 GMT
+            Not After : Nov  1 21:14:03 2001 GMT
+        Subject: C=NZ, L=Wellington, O=The Stuff of Culture and Taste, OU=SSL dev things, CN=tunala-server/Email=server@fake.domain
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+            RSA Public Key: (1024 bit)
+                Modulus (1024 bit):
+                    00:a7:07:a0:70:5e:76:4e:98:46:70:0e:53:e3:09:
+                    f4:18:7b:c2:7e:8a:d9:3c:12:52:24:9f:bf:76:e1:
+                    0e:ca:99:c0:ed:fb:0f:7c:d1:1a:6d:9c:1f:e0:19:
+                    3a:65:a4:00:26:19:5b:5f:0e:6d:a2:b0:49:0c:28:
+                    40:c5:43:94:55:57:50:d4:28:8a:35:3c:73:f3:bb:
+                    5a:14:1f:ac:85:ba:b1:20:26:98:9a:92:94:96:fb:
+                    90:1d:c4:18:04:f5:7e:f1:21:d7:34:5c:6e:14:b6:
+                    9b:48:ac:85:c0:50:d9:cf:79:67:a8:9e:3f:80:6c:
+                    ff:68:95:4c:52:95:c9:23:41
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            Netscape Comment: 
+                OpenSSL Generated Certificate
+            X509v3 Subject Key Identifier: 
+                9F:91:73:D0:8D:11:13:25:8B:68:2A:40:57:8D:96:B7:62:0B:6E:0E
+            X509v3 Authority Key Identifier: 
+                keyid:57:5A:5D:E0:71:0B:0C:E0:0F:51:87:DE:08:C8:25:E7:F7:87:46:C0
+                DirName:/C=NZ/L=Wellington/O=Really Irresponsible Authorisation Authority (RIAA)/OU=Cert-stamping/CN=Jackov al-Trades/Email=none@fake.domain
+                serial:00
+
+    Signature Algorithm: md5WithRSAEncryption
+        27:c3:c3:d8:2e:b2:12:0c:b0:4c:bd:a4:d0:34:77:fe:13:6f:
+        5e:8d:2c:50:c9:b5:a7:80:16:79:73:68:45:a1:5a:ed:74:d8:
+        7c:92:f4:04:c0:6b:da:a5:ca:6d:03:2c:cc:f9:32:f9:fb:b9:
+        be:83:5a:4b:c5:54:1d:07:02:a4:78:c4:90:49:f6:bb:0f:32:
+        57:86:ff:17:99:7a:a0:85:5f:ee:45:db:28:5f:91:66:d2:35:
+        54:d7:c2:55:26:ff:ea:b7:3a:d0:3f:fd:6a:dd:26:35:3f:18:
+        4e:c5:fa:36:d6:71:41:05:a0:4a:49:5d:b7:87:ee:6f:38:17:
+        9a:4a
+-----BEGIN CERTIFICATE-----
+MIIEDTCCA3agAwIBAgIBAjANBgkqhkiG9w0BAQQFADCBtDELMAkGA1UEBhMCTlox
+EzARBgNVBAcTCldlbGxpbmd0b24xPDA6BgNVBAoTM1JlYWxseSBJcnJlc3BvbnNp
+YmxlIEF1dGhvcmlzYXRpb24gQXV0aG9yaXR5IChSSUFBKTEWMBQGA1UECxMNQ2Vy
+dC1zdGFtcGluZzEZMBcGA1UEAxMQSmFja292IGFsLVRyYWRlczEfMB0GCSqGSIb3
+DQEJARYQbm9uZUBmYWtlLmRvbWFpbjAeFw0wMDExMDEyMTE0MDNaFw0wMTExMDEy
+MTE0MDNaMIGfMQswCQYDVQQGEwJOWjETMBEGA1UEBxMKV2VsbGluZ3RvbjEnMCUG
+A1UEChMeVGhlIFN0dWZmIG9mIEN1bHR1cmUgYW5kIFRhc3RlMRcwFQYDVQQLEw5T
+U0wgZGV2IHRoaW5nczEWMBQGA1UEAxMNdHVuYWxhLXNlcnZlcjEhMB8GCSqGSIb3
+DQEJARYSc2VydmVyQGZha2UuZG9tYWluMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB
+iQKBgQCnB6BwXnZOmEZwDlPjCfQYe8J+itk8ElIkn7924Q7KmcDt+w980RptnB/g
+GTplpAAmGVtfDm2isEkMKEDFQ5RVV1DUKIo1PHPzu1oUH6yFurEgJpiakpSW+5Ad
+xBgE9X7xIdc0XG4UtptIrIXAUNnPeWeonj+AbP9olUxSlckjQQIDAQABo4IBQDCC
+ATwwCQYDVR0TBAIwADAsBglghkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQg
+Q2VydGlmaWNhdGUwHQYDVR0OBBYEFJ+Rc9CNERMli2gqQFeNlrdiC24OMIHhBgNV
+HSMEgdkwgdaAFFdaXeBxCwzgD1GH3gjIJef3h0bAoYG6pIG3MIG0MQswCQYDVQQG
+EwJOWjETMBEGA1UEBxMKV2VsbGluZ3RvbjE8MDoGA1UEChMzUmVhbGx5IElycmVz
+cG9uc2libGUgQXV0aG9yaXNhdGlvbiBBdXRob3JpdHkgKFJJQUEpMRYwFAYDVQQL
+Ew1DZXJ0LXN0YW1waW5nMRkwFwYDVQQDExBKYWNrb3YgYWwtVHJhZGVzMR8wHQYJ
+KoZIhvcNAQkBFhBub25lQGZha2UuZG9tYWluggEAMA0GCSqGSIb3DQEBBAUAA4GB
+ACfDw9gushIMsEy9pNA0d/4Tb16NLFDJtaeAFnlzaEWhWu102HyS9ATAa9qlym0D
+LMz5Mvn7ub6DWkvFVB0HAqR4xJBJ9rsPMleG/xeZeqCFX+5F2yhfkWbSNVTXwlUm
+/+q3OtA//WrdJjU/GE7F+jbWcUEFoEpJXbeH7m84F5pK
+-----END CERTIFICATE-----
+
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQCnB6BwXnZOmEZwDlPjCfQYe8J+itk8ElIkn7924Q7KmcDt+w98
+0RptnB/gGTplpAAmGVtfDm2isEkMKEDFQ5RVV1DUKIo1PHPzu1oUH6yFurEgJpia
+kpSW+5AdxBgE9X7xIdc0XG4UtptIrIXAUNnPeWeonj+AbP9olUxSlckjQQIDAQAB
+AoGAJPm/PqDqt8Nl9HB3iY8uhiz/hVvfczjrDkh+7iYsTBV1bDlj2FHB8/nX5Jgx
+IUcI8WYzn/tlsdoskZBnpkOrlk0naOsgQk7Osh7Dn5sprRU0ieCgFhD/2DJNjJUE
+ZfHrjXwr8LeG2CMa/FzkfkjDPC40gT8plbTOXmLECrjiEnECQQDaukSpJHX4JVtL
+08j/mmKoBtBBVyzcSdxxHRXKBp+ivdszHC82oKH4yKwFdsfP6lX7iRsjKWIkdIiA
+M9ZDF4TLAkEAw34aSYXWJttRr91HZ+aDXbxTnaaqdrwP1hUJM7dH/a1DQDoxV1vR
+SWDX8YHSXgqPsxNtlW8bRtjh5cNWeZICowJAeS1A5MRCZxurHLNUY+dQSO6dadW2
+EMUKz8/hRm+s+ShdzE8NCSKtGXtzcgOAZ3vX7ubilfRTXwcAqKYPSlQ5SQJAFg2G
+p56abt201FLw+C6PySYyhIlFl4lVe//5fsUBMQO3n2oxILqx3EY5dfwAIC2u0jOx
+2/ahrBdRPFsRyTsIYwJBALAxKM5g7Qw0JgQ3gJnczY3/rpwlUhayYRYrNy1pJKp3
+hlVv+s5Oy1vXXppVfm830Eii7hsVp5PCFN+jRQhkFqw=
+-----END RSA PRIVATE KEY-----
diff --git a/demos/tunala/CA.pem b/demos/tunala/CA.pem
new file mode 100644 (file)
index 0000000..00d1e36
--- /dev/null
@@ -0,0 +1,24 @@
+-----BEGIN CERTIFICATE-----
+MIID9zCCA2CgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBtDELMAkGA1UEBhMCTlox
+EzARBgNVBAcTCldlbGxpbmd0b24xPDA6BgNVBAoTM1JlYWxseSBJcnJlc3BvbnNp
+YmxlIEF1dGhvcmlzYXRpb24gQXV0aG9yaXR5IChSSUFBKTEWMBQGA1UECxMNQ2Vy
+dC1zdGFtcGluZzEZMBcGA1UEAxMQSmFja292IGFsLVRyYWRlczEfMB0GCSqGSIb3
+DQEJARYQbm9uZUBmYWtlLmRvbWFpbjAeFw0wMDExMDEyMTA3MDdaFw0wMTExMDEy
+MTA3MDdaMIG0MQswCQYDVQQGEwJOWjETMBEGA1UEBxMKV2VsbGluZ3RvbjE8MDoG
+A1UEChMzUmVhbGx5IElycmVzcG9uc2libGUgQXV0aG9yaXNhdGlvbiBBdXRob3Jp
+dHkgKFJJQUEpMRYwFAYDVQQLEw1DZXJ0LXN0YW1waW5nMRkwFwYDVQQDExBKYWNr
+b3YgYWwtVHJhZGVzMR8wHQYJKoZIhvcNAQkBFhBub25lQGZha2UuZG9tYWluMIGf
+MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCj2aqRye4r8KZ0XKNC68elRVa5AHQY
+Igd2K5OYkxACU+UkGzkPoYUz6Uq/kFjXa0lftCxMBvr3FoPb/MMVD+0LeFcJQza5
+dMfp3abLe7mttgU2fAQzC6OMetXvqVaBp/rlT0AE2DhzA4g+p51ivkG/pw2zDpra
+nHaFsoRKg5puhQIDAQABo4IBFTCCAREwHQYDVR0OBBYEFFdaXeBxCwzgD1GH3gjI
+Jef3h0bAMIHhBgNVHSMEgdkwgdaAFFdaXeBxCwzgD1GH3gjIJef3h0bAoYG6pIG3
+MIG0MQswCQYDVQQGEwJOWjETMBEGA1UEBxMKV2VsbGluZ3RvbjE8MDoGA1UEChMz
+UmVhbGx5IElycmVzcG9uc2libGUgQXV0aG9yaXNhdGlvbiBBdXRob3JpdHkgKFJJ
+QUEpMRYwFAYDVQQLEw1DZXJ0LXN0YW1waW5nMRkwFwYDVQQDExBKYWNrb3YgYWwt
+VHJhZGVzMR8wHQYJKoZIhvcNAQkBFhBub25lQGZha2UuZG9tYWluggEAMAwGA1Ud
+EwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEARuRhKpXa7K2HZEvoIWlLxF/ZrpfB
+Zy2ixH25Uk8OnWm/NiS7eQAuR3aRWtEUsMENj1bC9I3hDk6zJVubp8TN0MPlvDvw
+gOsMkGGSoumZRPh2aQadcmdBAHSMUMxNHGG0VdhXhodYZCF0H7z0gvKMPVTuIH5U
+K2Iq6/aBOw9XbKM=
+-----END CERTIFICATE-----
diff --git a/demos/tunala/Makefile b/demos/tunala/Makefile
new file mode 100644 (file)
index 0000000..fd5b651
--- /dev/null
@@ -0,0 +1,40 @@
+# Edit these to suit
+#
+# Oh yeah, and please read the README too.
+
+
+SSL_HOMEDIR=../..
+SSL_INCLUDEDIR=$(SSL_HOMEDIR)/include
+SSL_LIBDIR=$(SSL_HOMEDIR)
+
+RM=rm -f
+CC=gcc
+DEBUG_FLAGS=-g -ggdb3 -Wall -Wshadow
+INCLUDE_FLAGS=-I$(SSL_INCLUDEDIR)
+CFLAGS=$(DEBUG_FLAGS) $(INCLUDE_FLAGS)
+COMPILE=$(CC) $(CFLAGS) -c
+
+# Edit, particularly the "-ldl" if not building with "dlfcn" support
+LINK_FLAGS=-L$(SSL_LIBDIR) -lssl -lcrypto -ldl
+
+SRCS=buffer.c ip.c sm.c tunala.c
+OBJS=buffer.o ip.o sm.o tunala.o
+
+TARGETS=tunala
+
+default: $(TARGETS)
+
+clean:
+       $(RM) $(OBJS) $(TARGETS) *.bak core
+
+.c.o:
+       $(COMPILE) $<
+
+tunala: $(OBJS)
+       $(CC) -o tunala $(OBJS) $(LINK_FLAGS)
+
+# Extra dependencies, should really use makedepend
+buffer.o: buffer.c tunala.h
+ip.o: ip.c tunala.h
+sm.o: sm.c tunala.h
+tunala.o: tunala.c tunala.h
diff --git a/demos/tunala/buffer.c b/demos/tunala/buffer.c
new file mode 100644 (file)
index 0000000..e9a4e5b
--- /dev/null
@@ -0,0 +1,146 @@
+#include "tunala.h"
+
+#ifndef NO_BUFFER
+
+void buffer_init(buffer_t *buf)
+{
+       buf->used = 0;
+}
+
+void buffer_close(buffer_t *buf)
+{
+       /* Our data is static - nothing needs "release", just reset */
+       buffer_init(buf);
+}
+
+/* Code these simple ones in compact form */
+unsigned int buffer_used(buffer_t *buf) {
+       return buf->used; }
+unsigned int buffer_unused(buffer_t *buf) {
+       return (MAX_DATA_SIZE - buf->used); }
+int buffer_full(buffer_t *buf) {
+       return (buf->used == MAX_DATA_SIZE ? 1 : 0); }
+int buffer_notfull(buffer_t *buf) {
+       return (buf->used < MAX_DATA_SIZE ? 1 : 0); }
+int buffer_empty(buffer_t *buf) {
+       return (buf->used == 0 ? 1 : 0); }
+int buffer_notempty(buffer_t *buf) {
+       return (buf->used > 0 ? 1 : 0); }
+
+unsigned int buffer_adddata(buffer_t *buf, const unsigned char *ptr,
+               unsigned int size)
+{
+       unsigned int added = MAX_DATA_SIZE - buf->used;
+       if(added > size)
+               added = size;
+       if(added == 0)
+               return 0;
+       memcpy(buf->data + buf->used, ptr, added);
+       buf->used += added;
+       return added;
+}
+
+unsigned int buffer_takedata(buffer_t *buf, unsigned char *ptr,
+               unsigned int size)
+{
+       unsigned int taken = buf->used;
+       if(taken > size)
+               taken = size;
+       if(taken == 0)
+               return 0;
+       if(ptr)
+               memcpy(ptr, buf->data, taken);
+       buf->used -= taken;
+       /* Do we have to scroll? */
+       if(buf->used > 0)
+               memmove(buf->data, buf->data + taken, buf->used);
+       return taken;
+}
+
+unsigned int buffer_tobuffer(buffer_t *to, buffer_t *from, int cap)
+{
+       unsigned int moved, tomove = from->used;
+       if((int)tomove > cap)
+               tomove = cap;
+       if(tomove == 0)
+               return 0;
+       moved = buffer_adddata(to, from->data, tomove);
+       if(moved == 0)
+               return 0;
+       buffer_takedata(from, NULL, moved);
+       return moved;
+}
+
+#ifndef NO_IP
+
+int buffer_from_fd(buffer_t *buf, int fd)
+{
+       unsigned int toread = buffer_unused(buf);
+       if(toread == 0)
+               /* Shouldn't be called in this case! */
+               abort();
+       toread = read(fd, buf->data + buf->used, toread);
+       if(toread > 0)
+               buf->used += toread;
+       return toread;
+}
+
+int buffer_to_fd(buffer_t *buf, int fd)
+{
+       unsigned int towrite = buffer_used(buf);
+       if(towrite == 0)
+               /* Shouldn't be called in this case! */
+               abort();
+       towrite = write(fd, buf->data, towrite);
+       if(towrite > 0)
+               buffer_takedata(buf, NULL, towrite);
+       return towrite;
+}
+
+#endif /* !defined(NO_IP) */
+
+#ifndef NO_OPENSSL
+
+void buffer_from_SSL(buffer_t *buf, SSL *ssl)
+{
+       int ret;
+       if(!ssl || buffer_full(buf))
+               return;
+       ret = SSL_read(ssl, buf->data + buf->used, buffer_unused(buf));
+       if(ret > 0)
+               buf->used += ret;
+}
+
+void buffer_to_SSL(buffer_t *buf, SSL *ssl)
+{
+       int ret;
+       if(!ssl || buffer_empty(buf))
+               return;
+       ret = SSL_write(ssl, buf->data, buf->used);
+       if(ret > 0)
+               buffer_takedata(buf, NULL, ret);
+}
+
+void buffer_from_BIO(buffer_t *buf, BIO *bio)
+{
+       int ret;
+       if(!bio || buffer_full(buf))
+               return;
+       ret = BIO_read(bio, buf->data + buf->used, buffer_unused(buf));
+       if(ret > 0)
+               buf->used += ret;
+}
+
+void buffer_to_BIO(buffer_t *buf, BIO *bio)
+{
+       int ret;
+       if(!bio || buffer_empty(buf))
+               return;
+       ret = BIO_write(bio, buf->data, buf->used);
+       if(ret > 0)
+               buffer_takedata(buf, NULL, ret);
+}
+
+#endif /* !defined(NO_OPENSSL) */
+
+#endif /* !defined(NO_BUFFER) */
diff --git a/demos/tunala/ip.c b/demos/tunala/ip.c
new file mode 100644 (file)
index 0000000..4874d62
--- /dev/null
@@ -0,0 +1,147 @@
+#include "tunala.h"
+
+#ifndef NO_IP
+
+#define IP_LISTENER_BACKLOG 511 /* So if it gets masked by 256 or some other
+                                  such value it'll still be respectable */
+
+/* Any IP-related initialisations. For now, this means blocking SIGPIPE */
+int ip_initialise(void)
+{
+       struct sigaction sa;
+
+       sa.sa_handler = SIG_IGN;
+       sa.sa_flags = 0;
+       sigemptyset(&sa.sa_mask);
+       if(sigaction(SIGPIPE, &sa, NULL) != 0)
+               return 0;
+       return 1;
+}
+
+int ip_create_listener_split(const unsigned char *ip, unsigned short port)
+{
+       struct sockaddr_in in_addr;
+       int fd = -1;
+       int reuseVal = 1;
+
+       /* Create the socket */
+       if((fd = socket(PF_INET, SOCK_STREAM, 0)) == -1)
+               goto err;
+       /* Set the SO_REUSEADDR flag - servers act weird without it */
+       if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)(&reuseVal),
+                               sizeof(reuseVal)) != 0)
+               goto err;
+       /* Prepare the listen address stuff */
+       in_addr.sin_family = AF_INET;
+       memcpy(&in_addr.sin_addr.s_addr, ip, 4);
+       in_addr.sin_port = htons(port);
+       /* Bind to the required port/address/interface */
+       if(bind(fd, &in_addr, sizeof(struct sockaddr_in)) != 0)
+               goto err;
+       /* Start "listening" */
+       if(listen(fd, IP_LISTENER_BACKLOG) != 0)
+               goto err;
+       return fd;
+err:
+       if(fd != -1)
+               close(fd);
+       return -1;
+}
+
+int ip_create_connection_split(const unsigned char *ip, unsigned short port)
+{
+       struct sockaddr_in in_addr;
+       int flags, fd = -1;
+
+       /* Create the socket */
+       if((fd = socket(PF_INET, SOCK_STREAM, 0)) == -1)
+               goto err;
+       /* Make it non-blocking */
+       if(((flags = fcntl(fd, F_GETFL, 0)) < 0) ||
+                       (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0))
+               goto err;
+       /* Prepare the connection address stuff */
+       in_addr.sin_family = AF_INET;
+       memcpy(&in_addr.sin_addr.s_addr, ip, 4);
+       in_addr.sin_port = htons(port);
+       /* Start a connect (non-blocking, in all likelihood) */
+       if((connect(fd, &in_addr, sizeof(struct sockaddr_in)) != 0) &&
+                       (errno != EINPROGRESS))
+               goto err;
+       return fd;
+err:
+       if(fd != -1)
+               close(fd);
+       return -1;
+}
+
+static unsigned char all_local_ip[] = {0x00,0x00,0x00,0x00};
+
+int ip_parse_address(const char *address, unsigned char **parsed_ip,
+               unsigned short *parsed_port, int accept_all_ip)
+{
+       char buf[256];
+       struct hostent *lookup;
+       unsigned long port;
+       char *temp;
+       const char *ptr = strstr(address, ":");
+       unsigned char *ip = all_local_ip;
+
+       if(!ptr) {
+               /* We assume we're listening on all local interfaces and have
+                * only specified a port. */
+               if(!accept_all_ip)
+                       return 0;
+               ptr = address;
+               goto determine_port;
+       }
+       if((ptr - address) > 255)
+               return 0;
+       memset(buf, 0, 256);
+       memcpy(buf, address, ptr - address);
+       ptr++;
+       if((lookup = gethostbyname(buf)) == NULL) {
+               /* Spit a message to differentiate between lookup failures and
+                * bad strings. */
+               fprintf(stderr, "hostname lookup for '%s' failed\n", buf);
+               return 0;
+       }
+       ip = lookup->h_addr_list[0];
+determine_port:
+       if(strlen(ptr) < 1)
+               return 0;
+       port = strtoul(ptr, &temp, 10);
+       if((temp == ptr) || (*temp != '\0') || (port > 65535))
+               return 0;
+       *parsed_ip = ip;
+       *parsed_port = (unsigned short)port;
+       return 1;
+}
+
+int ip_create_listener(const char *address)
+{
+       unsigned char *ip;
+       unsigned short port;
+
+       if(!ip_parse_address(address, &ip, &port, 1))
+               return -1;
+       return ip_create_listener_split(ip, port);
+}
+
+int ip_create_connection(const char *address)
+{
+       unsigned char *ip;
+       unsigned short port;
+
+       if(!ip_parse_address(address, &ip, &port, 0))
+               return -1;
+       return ip_create_connection_split(ip, port);
+}
+
+int ip_accept_connection(int listen_fd)
+{
+       return accept(listen_fd, NULL, NULL);
+}
+
+#endif /* !defined(NO_IP) */
+
diff --git a/demos/tunala/sm.c b/demos/tunala/sm.c
new file mode 100644 (file)
index 0000000..05bd7b9
--- /dev/null
@@ -0,0 +1,149 @@
+#include "tunala.h"
+
+#ifndef NO_TUNALA
+
+void state_machine_init(state_machine_t *machine)
+{
+       machine->ssl = NULL;
+       machine->bio_intossl = machine->bio_fromssl = NULL;
+       buffer_init(&machine->clean_in);
+       buffer_init(&machine->clean_out);
+       buffer_init(&machine->dirty_in);
+       buffer_init(&machine->dirty_out);
+}
+
+void state_machine_close(state_machine_t *machine)
+{
+       if(machine->ssl)
+               SSL_free(machine->ssl);
+/* SSL_free seems to decrement the reference counts already so doing this goes
+ * kaboom. */
+#if 0
+       if(machine->bio_intossl)
+               BIO_free(machine->bio_intossl);
+       if(machine->bio_fromssl)
+               BIO_free(machine->bio_fromssl);
+#endif
+       buffer_close(&machine->clean_in);
+       buffer_close(&machine->clean_out);
+       buffer_close(&machine->dirty_in);
+       buffer_close(&machine->dirty_out);
+       state_machine_init(machine);
+}
+
+buffer_t *state_machine_get_buffer(state_machine_t *machine, sm_buffer_t type)
+{
+       switch(type) {
+       case SM_CLEAN_IN:
+               return &machine->clean_in;
+       case SM_CLEAN_OUT:
+               return &machine->clean_out;
+       case SM_DIRTY_IN:
+               return &machine->dirty_in;
+       case SM_DIRTY_OUT:
+               return &machine->dirty_out;
+       default:
+               break;
+       }
+       /* Should never get here */
+       abort();
+       return NULL;
+}
+
+SSL *state_machine_get_SSL(state_machine_t *machine)
+{
+       return machine->ssl;
+}
+
+void state_machine_set_SSL(state_machine_t *machine, SSL *ssl, int is_server)
+{
+       if(machine->ssl)
+               /* Shouldn't ever be set twice */
+               abort();
+       machine->ssl = ssl;
+       /* Create the BIOs to handle the dirty side of the SSL */
+       if((machine->bio_intossl = BIO_new(BIO_s_mem())) == NULL)
+               abort();
+       if((machine->bio_fromssl = BIO_new(BIO_s_mem())) == NULL)
+               abort();
+       /* Hook up the BIOs on the dirty side of the SSL */
+       SSL_set_bio(machine->ssl, machine->bio_intossl, machine->bio_fromssl);
+       if(is_server)
+               SSL_set_accept_state(machine->ssl);
+       else
+               SSL_set_connect_state(machine->ssl);
+       /* If we're the first one to generate traffic - do it now otherwise we
+        * go into the next select empty-handed and our peer will not send data
+        * but will similarly wait for us. */
+       state_machine_churn(machine);
+}
+
+/* Performs the data-IO loop and returns zero if the machine should close */
+int state_machine_churn(state_machine_t *machine)
+{
+       unsigned int loop;
+       /* Do this loop twice to cover any dependencies about which precise
+        * order of reads and writes is required. */
+       for(loop = 0; loop < 2; loop++) {
+               buffer_to_SSL(&machine->clean_in, machine->ssl);
+               buffer_to_BIO(&machine->dirty_in, machine->bio_intossl);
+               buffer_from_SSL(&machine->clean_out, machine->ssl);
+               buffer_from_BIO(&machine->dirty_out, machine->bio_fromssl);
+       }
+       if(machine->ssl == NULL) {
+               if(buffer_empty(&machine->clean_out))
+                       /* Time to close this state-machine altogether */
+                       return 0;
+               else
+                       /* Still buffered data on the clean side to go out */
+                       return 1;
+       }
+       if(SSL_get_shutdown(machine->ssl)) {
+               /* An SSL shutdown was underway */
+               if(buffer_empty(&machine->dirty_out)) {
+                       /* Great, we can seal off the dirty side completely */
+                       if(!state_machine_close_dirty(machine))
+                               return 0;
+               }
+       }
+       /* Either the SSL is alive and well, or the closing process still has
+        * outgoing data waiting to be sent */
+       return 1;
+}
+
+/* Called when the clean side of the SSL has lost its connection */
+int state_machine_close_clean(state_machine_t *machine)
+{
+       /* Well, first thing to do is null out the clean-side buffers - they're
+        * no use any more. */
+       buffer_close(&machine->clean_in);
+       buffer_close(&machine->clean_out);
+       /* And start an SSL shutdown */
+       SSL_shutdown(machine->ssl);
+       /* This is an "event", so flush the SSL of any generated traffic */
+       state_machine_churn(machine);
+       if(buffer_empty(&machine->dirty_in) &&
+                       buffer_empty(&machine->dirty_out))
+               return 0;
+       return 1;
+}
+
+/* Called when the dirty side of the SSL has lost its connection. This is pretty
+ * terminal as all that can be left to do is send any buffered output on the
+ * clean side - after that, we're done. */
+int state_machine_close_dirty(state_machine_t *machine)
+{
+       buffer_close(&machine->dirty_in);
+       buffer_close(&machine->dirty_out);
+       buffer_close(&machine->clean_in);
+       if(machine->ssl)
+               SSL_free(machine->ssl);
+       machine->ssl = NULL;
+       machine->bio_intossl = machine->bio_fromssl = NULL;
+       if(buffer_empty(&machine->clean_out))
+               return 0;
+       return 1;
+}
+
+#endif /* !defined(NO_TUNALA) */
+
diff --git a/demos/tunala/tunala.c b/demos/tunala/tunala.c
new file mode 100644 (file)
index 0000000..445940f
--- /dev/null
@@ -0,0 +1,733 @@
+#if defined(NO_BUFFER) || defined(NO_IP) || defined(NO_OPENSSL)
+#error "Badness, NO_BUFFER, NO_IP or NO_OPENSSL is defined, turn them *off*"
+#endif
+
+/* Include our bits'n'pieces */
+#include "tunala.h"
+
+
+/********************************************/
+/* Our local types that specify our "world" */
+/********************************************/
+
+/* These represent running "tunnels". Eg. if you wanted to do SSL in a
+ * "message-passing" scanario, the "int" file-descriptors might be replaced by
+ * thread or process IDs, and the "select" code might be replaced by message
+ * handling code. Whatever. */
+typedef struct _tunala_item_t {
+       /* The underlying SSL state machine. This is a data-only processing unit
+        * and we communicate with it by talking to its four "buffers". */
+       state_machine_t sm;
+       /* The file-descriptors for the "dirty" (encrypted) side of the SSL
+        * setup. In actuality, this is typically a socket and both values are
+        * identical. */
+       int dirty_read, dirty_send;
+       /* The file-descriptors for the "clean" (unencrypted) side of the SSL
+        * setup. These could be stdin/stdout, a socket (both values the same),
+        * or whatever you like. */
+       int clean_read, clean_send;
+} tunala_item_t;
+
+/* This structure is used as the data for running the main loop. Namely, in a
+ * network format such as this, it is stuff for select() - but as pointed out,
+ * when moving the real-world to somewhere else, this might be replaced by
+ * something entirely different. It's basically the stuff that controls when
+ * it's time to do some "work". */
+typedef struct _select_sets_t {
+       int max; /* As required as the first argument to select() */
+       fd_set reads, sends, excepts; /* As passed to select() */
+} select_sets_t;
+typedef struct _tunala_selector_t {
+       select_sets_t last_selected; /* Results of the last select() */
+       select_sets_t next_select; /* What we'll next select on */
+} tunala_selector_t;
+
+/* This structure is *everything*. We do it to avoid the use of globals so that,
+ * for example, it would be easier to shift things around between async-IO,
+ * thread-based, or multi-fork()ed (or combinations thereof). */
+typedef struct _tunala_world_t {
+       /* The file-descriptor we "listen" on for new connections */
+       int listen_fd;
+       /* The array of tunnels */
+       tunala_item_t *tunnels;
+       /* the number of tunnels in use and allocated, respectively */
+       unsigned int tunnels_used, tunnels_size;
+       /* Our outside "loop" context stuff */
+       tunala_selector_t selector;
+       /* Our SSL_CTX, which is configured as the SSL client or server and has
+        * the various cert-settings and callbacks configured. */
+       SSL_CTX *ssl_ctx;
+       /* Simple flag with complex logic :-) Indicates whether we're an SSL
+        * server or an SSL client. */
+       int server_mode;
+} tunala_world_t;
+
+/*****************************/
+/* Internal static functions */
+/*****************************/
+
+static SSL_CTX *initialise_ssl_ctx(int server_mode, const char *engine_id,
+               const char *CAfile, const char *cert, const char *key);
+static void selector_init(tunala_selector_t *selector);
+static void selector_add_listener(tunala_selector_t *selector, int fd);
+static void selector_add_tunala(tunala_selector_t *selector, tunala_item_t *t);
+static int selector_select(tunala_selector_t *selector);
+/* This returns -1 for error, 0 for no new connections, or 1 for success, in
+ * which case *newfd is populated. */
+static int selector_get_listener(tunala_selector_t *selector, int fd, int *newfd);
+static int tunala_world_new_item(tunala_world_t *world, int fd,
+               const unsigned char *ip, unsigned short port);
+static void tunala_world_del_item(tunala_world_t *world, unsigned int idx);
+static int tunala_item_io(tunala_selector_t *selector, tunala_item_t *item);
+
+/*********************************************/
+/* MAIN FUNCTION (and its utility functions) */
+/*********************************************/
+
+/* For now, hard-coded as follows;
+ * (a) We're like "tunala -listen 127.0.0.1:9001 -proxy 127.0.0.1:9002"
+ * (b) We max out at 50 simultaneous tunnels, listening will stop while we have
+ *     that many tunnels operating and will recommence as/when tunnels close.
+ * (c) We are an SSL client proxy
+ * (d) We use the "openssl" ENGINE
+ * (e) We use the CA cert file "cacert.pem"
+ * */
+
+static const char *def_proxyhost = "127.0.0.1:443";
+static const char *def_listenhost = "127.0.0.1:8080";
+static int def_max_tunnels = 50;
+static const char *def_cacert = NULL;
+static const char *def_cert = NULL;
+static const char *def_key = NULL;
+static const char *def_engine_id = NULL;
+static int def_server_mode = 0;
+
+static const char *helpstring =
+       "\n'Tunala' (A tunneler with a New Zealand accent)\n"
+       "Usage: tunala [options], where options are from;\n"
+       "    -listen [host:]<port>  (default = 127.0.0.1:8080)\n"
+       "    -proxy <host>:<port>   (default = 127.0.0.1:443)\n"
+       "    -maxtunnels <num>      (default = 50)\n"
+       "    -cacert <path|NULL>    (default = NULL)\n"
+       "    -cert <path|NULL>      (default = NULL)\n"
+       "    -key <path|NULL>       (default = whatever '-cert' is)\n"
+       "    -engine <id|NULL>      (default = NULL)\n"
+       "    -server <0|1>          (default = 0, ie. an SSL client)\n"
+       "    -<h|help|?>            (displays this help screen)\n"
+       "NB: It is recommended to specify a cert+key when operating as an\n"
+       "SSL server. If you only specify '-cert', the same file must\n"
+       "contain a matching private key.\n";
+
+static int usage(const char *errstr, int isunknownarg)
+{
+       if(isunknownarg)
+               fprintf(stderr, "Error: unknown argument '%s'\n", errstr);
+       else
+               fprintf(stderr, "Error: %s\n", errstr);
+       fprintf(stderr, "%s\n", helpstring);
+       return 1;
+}
+
+static int err_str0(const char *str0)
+{
+       fprintf(stderr, str0);
+       fprintf(stderr, "\n");
+       return 1;
+}
+
+static int err_str1(const char *str0, const char *str1)
+{
+       fprintf(stderr, str0, str1);
+       fprintf(stderr, "\n");
+       return 1;
+}
+
+static int parse_max_tunnels(const char *s, unsigned int *maxtunnels)
+{
+       unsigned long l;
+       char *temp;
+       l = strtoul(s, &temp, 10);
+       if((temp == s) || (*temp != '\0') || (l < 1) || (l > 1024)) {
+               fprintf(stderr, "Error, '%s' is an invalid value for "
+                               "maxtunnels\n", s);
+               return 0;
+       }
+       *maxtunnels = (unsigned int)l;
+       return 1;
+}
+
+static int parse_server_mode(const char *s, int *servermode)
+{
+       unsigned long l;
+       char *temp;
+       l = strtoul(s, &temp, 10);
+       if((temp == s) || (*temp != '\0') || (l > 1)) {
+               fprintf(stderr, "Error, '%s' is an invalid value for the "
+                               "server mode\n", s);
+               return 0;
+       }
+       *servermode = (int)l;
+       return 1;
+}
+
+int main(int argc, char *argv[])
+{
+       unsigned int loop;
+       int newfd;
+       tunala_world_t world;
+       tunala_item_t *t_item;
+       unsigned char *proxy_ip;
+       unsigned short proxy_port;
+       /* Overridables */
+       const char *proxyhost = def_proxyhost;
+       const char *listenhost = def_listenhost;
+       unsigned int max_tunnels = def_max_tunnels;
+       const char *cacert = def_cacert;
+       const char *cert = def_cert;
+       const char *key = def_key;
+       const char *engine_id = def_engine_id;
+       int server_mode = def_server_mode;
+
+/* Parse command-line arguments */
+next_arg:
+       argc--; argv++;
+       if(argc > 0) {
+               if(strcmp(*argv, "-listen") == 0) {
+                       if(argc < 2)
+                               return usage("-listen requires an argument", 0);
+                       argc--; argv++;
+                       listenhost = *argv;
+                       goto next_arg;
+               } else if(strcmp(*argv, "-proxy") == 0) {
+                       if(argc < 2)
+                               return usage("-proxy requires an argument", 0);
+                       argc--; argv++;
+                       proxyhost = *argv;
+                       goto next_arg;
+               } else if(strcmp(*argv, "-maxtunnels") == 0) {
+                       if(argc < 2)
+                               return usage("-maxtunnels requires an argument", 0);
+                       argc--; argv++;
+                       if(!parse_max_tunnels(*argv, &max_tunnels))
+                               return 1;
+                       goto next_arg;
+               } else if(strcmp(*argv, "-cacert") == 0) {
+                       if(argc < 2)
+                               return usage("-cacert requires an argument", 0);
+                       argc--; argv++;
+                       if(strcmp(*argv, "NULL") == 0)
+                               cacert = NULL;
+                       else
+                               cacert = *argv;
+                       goto next_arg;
+               } else if(strcmp(*argv, "-cert") == 0) {
+                       if(argc < 2)
+                               return usage("-cert requires an argument", 0);
+                       argc--; argv++;
+                       if(strcmp(*argv, "NULL") == 0)
+                               cert = NULL;
+                       else
+                               cert = *argv;
+                       goto next_arg;
+               } else if(strcmp(*argv, "-key") == 0) {
+                       if(argc < 2)
+                               return usage("-key requires an argument", 0);
+                       argc--; argv++;
+                       if(strcmp(*argv, "NULL") == 0)
+                               key = NULL;
+                       else
+                               key = *argv;
+                       goto next_arg;
+               } else if(strcmp(*argv, "-engine") == 0) {
+                       if(argc < 2)
+                               return usage("-engine requires an argument", 0);
+                       argc--; argv++;
+                       engine_id = *argv;
+                       goto next_arg;
+               } else if(strcmp(*argv, "-server") == 0) {
+                       if(argc < 2)
+                               return usage("-server requires an argument", 0);
+                       argc--; argv++;
+                       if(!parse_server_mode(*argv, &server_mode))
+                               return 1;
+                       goto next_arg;
+               } else if((strcmp(*argv, "-h") == 0) ||
+                               (strcmp(*argv, "-help") == 0) ||
+                               (strcmp(*argv, "-?") == 0)) {
+                       fprintf(stderr, "%s\n", helpstring);
+                       return 0;
+               } else
+                       return usage(*argv, 1);
+       }
+
+       /* Initialise network stuff */
+       if(!ip_initialise())
+               return err_str0("ip_initialise failed");
+       err_str0("ip_initialise succeeded");
+       /* Create the SSL_CTX */
+       if((world.ssl_ctx = initialise_ssl_ctx(server_mode, engine_id,
+                       cacert, cert, key)) == NULL)
+               return err_str1("initialise_ssl_ctx(engine_id=%s) failed",
+                       (engine_id == NULL) ? "NULL" : engine_id);
+       err_str1("initialise_ssl_ctx(engine_id=%s) succeeded",
+                       (engine_id == NULL) ? "NULL" : engine_id);
+       /* Create the listener */
+       if((world.listen_fd = ip_create_listener(listenhost)) == -1)
+               return err_str1("ip_create_listener(%s) failed", listenhost);
+       err_str1("ip_create_listener(%s) succeeded", listenhost);
+       if(!ip_parse_address(proxyhost, &proxy_ip, &proxy_port, 0))
+               return err_str1("ip_parse_address(%s) failed", proxyhost);
+       err_str1("ip_parse_address(%s) succeeded", proxyhost);
+       fprintf(stderr, "Info - proxying to %d.%d.%d.%d:%d\n",
+                       (int)proxy_ip[0], (int)proxy_ip[1],
+                       (int)proxy_ip[2], (int)proxy_ip[3], (int)proxy_port);
+       fprintf(stderr, "Info - set maxtunnels to %d\n", (int)max_tunnels);
+       fprintf(stderr, "Info - set to operate as an SSL %s\n",
+                       (server_mode ? "server" : "client"));
+       /* Initialise the rest of the stuff */
+       world.tunnels_used = world.tunnels_size = 0;
+       world.tunnels = NULL;
+       world.server_mode = server_mode;
+       selector_init(&world.selector);
+
+/* We're ready to loop */
+main_loop:
+       /* Should we listen for *new* tunnels? */
+       if(world.tunnels_used < max_tunnels)
+               selector_add_listener(&world.selector, world.listen_fd);
+       /* We should add in our existing tunnels */
+       for(loop = 0; loop < world.tunnels_used; loop++)
+               selector_add_tunala(&world.selector, world.tunnels + loop);
+       /* Now do the select */
+       switch(selector_select(&world.selector)) {
+       case -1:
+               fprintf(stderr, "selector_select returned a badness error.\n");
+               abort();
+       case 0:
+               fprintf(stderr, "Warn, selector_select return 0 - signal??\n");
+               goto main_loop;
+       default:
+               break;
+       }
+       /* Accept new connection if we should and can */
+       if((world.tunnels_used < max_tunnels) && (selector_get_listener(
+                                       &world.selector, world.listen_fd,
+                                       &newfd) == 1)) {
+               /* We have a new connection */
+               if(!tunala_world_new_item(&world, newfd,
+                                       proxy_ip, proxy_port)) {
+                       fprintf(stderr, "tunala_world_new_item failed\n");
+                       abort();
+               }
+               fprintf(stderr, "Info, new tunnel opened, now up to %d\n",
+                               world.tunnels_used);
+       }
+       /* Give each tunnel its moment, note the while loop is because it makes
+        * the logic easier than with "for" to deal with an array that may shift
+        * because of deletes. */
+       loop = 0;
+       t_item = world.tunnels;
+       while(loop < world.tunnels_used) {
+               if(!tunala_item_io(&world.selector, t_item)) {
+                       /* We're closing whether for reasons of an error or a
+                        * natural close. Don't increment loop or t_item because
+                        * the next item is moving to us! */
+                       tunala_world_del_item(&world, loop);
+                       fprintf(stderr, "Info, tunnel closed, down to %d\n",
+                                       world.tunnels_used);
+               }
+               else {
+                       /* Move to the next item */
+                       loop++;
+                       t_item++;
+               }
+       }
+       goto main_loop;
+       /* Should never get here */
+       abort();
+       return 1;
+}
+
+/****************/
+/* OpenSSL bits */
+/****************/
+
+static SSL_CTX *initialise_ssl_ctx(int server_mode, const char *engine_id,
+               const char *CAfile, const char *cert, const char *key)
+{
+       SSL_CTX *ctx, *ret = NULL;
+       SSL_METHOD *meth;
+       ENGINE *e = NULL;
+       FILE *fp = NULL;
+       X509 *x509 = NULL;
+       EVP_PKEY *pkey = NULL;
+
+        OpenSSL_add_ssl_algorithms();
+        SSL_load_error_strings();
+
+       meth = (server_mode ? SSLv23_server_method() : SSLv23_client_method());
+       if(meth == NULL)
+               goto err;
+       if(engine_id) {
+               if((e = ENGINE_by_id(engine_id)) == NULL) {
+                       fprintf(stderr, "Error obtaining '%s' engine, openssl "
+                                       "errors follow\n", engine_id);
+                       goto err;
+               }
+               if(!ENGINE_set_default(e, ENGINE_METHOD_ALL)) {
+                       fprintf(stderr, "Error assigning '%s' engine, openssl "
+                                       "errors follow\n", engine_id);
+                       goto err;
+               }
+               ENGINE_free(e);
+       }
+       if((ctx = SSL_CTX_new(meth)) == NULL)
+               goto err;
+       /* cacert */
+       if(CAfile) {
+               if(!X509_STORE_load_locations(SSL_CTX_get_cert_store(ctx),
+                                       CAfile, NULL)) {
+                       fprintf(stderr, "Error loading CA cert(s) in '%s'\n",
+                                       CAfile);
+                       goto err;
+               }
+               fprintf(stderr, "Info, operating with CA cert(s) in '%s'\n",
+                               CAfile);
+       } else
+               fprintf(stderr, "Info, operating without a CA cert(-list)\n");
+       if(!SSL_CTX_set_default_verify_paths(ctx)) {
+               fprintf(stderr, "Error setting default verify paths\n");
+               goto err;
+       }
+       /* cert */
+       if(cert) {
+               if((fp = fopen(cert, "r")) == NULL) {
+                       fprintf(stderr, "Error opening cert file '%s'\n", cert);
+                       goto err;
+               }
+               if(!PEM_read_X509(fp, &x509, NULL, NULL)) {
+                       fprintf(stderr, "Error reading PEM cert from '%s'\n",
+                                       cert);
+                       goto err;
+               }
+               if(!SSL_CTX_use_certificate(ctx, x509)) {
+                       fprintf(stderr, "Error, cert in '%s' can not be used\n",
+                                       cert);
+                       goto err;
+               }
+               fprintf(stderr, "Info, operating with cert in '%s'\n", cert);
+               fclose(fp);
+               fp = NULL;
+               /* If a cert was given without matching key, we assume the same
+                * file contains the required key. */
+               if(!key)
+                       key = cert;
+       } else
+               if(key) {
+                       fprintf(stderr, "Error, can't specify a key without a "
+                                       "corresponding certificate\n");
+                       goto err;
+               }
+       /* key */
+       if(key) {
+               if((fp = fopen(key, "r")) == NULL) {
+                       fprintf(stderr, "Error opening key file '%s'\n", key);
+                       goto err;
+               }
+               if(!PEM_read_PrivateKey(fp, &pkey, NULL, NULL)) {
+                       fprintf(stderr, "Error reading PEM key from '%s'\n",
+                                       key);
+                       goto err;
+               }
+               if(!SSL_CTX_use_PrivateKey(ctx, pkey)) {
+                       fprintf(stderr, "Error, key in '%s' can not be used\n",
+                                       key);
+                       goto err;
+               }
+               fprintf(stderr, "Info, operating with key in '%s'\n", key);
+       } else
+               fprintf(stderr, "Info, operating without a cert or key\n");
+
+       /* Success! */
+       ret = ctx;
+err:
+       if(!ret) {
+               ERR_print_errors_fp(stderr);
+               if(ctx)
+                       SSL_CTX_free(ctx);
+       }
+       if(fp)
+               fclose(fp);
+       if(x509)
+               X509_free(x509);
+       if(pkey)
+               EVP_PKEY_free(pkey);
+       return ret;
+}
+
+/*****************/
+/* Selector bits */
+/*****************/
+
+static void selector_sets_init(select_sets_t *s)
+{
+       s->max = 0;
+       FD_ZERO(&s->reads);
+       FD_ZERO(&s->sends);
+       FD_ZERO(&s->excepts);
+}
+static void selector_init(tunala_selector_t *selector)
+{
+       selector_sets_init(&selector->last_selected);
+       selector_sets_init(&selector->next_select);
+}
+
+#define SEL_EXCEPTS 0x00
+#define SEL_READS   0x01
+#define SEL_SENDS   0x02
+static void selector_add_raw_fd(tunala_selector_t *s, int fd, int flags)
+{
+       FD_SET(fd, &s->next_select.excepts);
+       if(flags & SEL_READS)
+               FD_SET(fd, &s->next_select.reads);
+       if(flags & SEL_SENDS)
+               FD_SET(fd, &s->next_select.sends);
+       /* Adjust "max" */
+       if(s->next_select.max < (fd + 1))
+               s->next_select.max = fd + 1;
+}
+
+static void selector_add_listener(tunala_selector_t *selector, int fd)
+{
+       selector_add_raw_fd(selector, fd, SEL_READS);
+}
+
+static void selector_add_tunala(tunala_selector_t *s, tunala_item_t *t)
+{
+       /* Set clean read if sm.clean_in is not full */
+       if(t->clean_read != -1) {
+               selector_add_raw_fd(s, t->clean_read,
+                       (buffer_full(state_machine_get_buffer(&t->sm,
+                               SM_CLEAN_IN)) ? SEL_EXCEPTS : SEL_READS));
+       }
+       /* Set clean send if sm.clean_out is not empty */
+       if(t->clean_send != -1) {
+               selector_add_raw_fd(s, t->clean_send,
+                       (buffer_empty(state_machine_get_buffer(&t->sm,
+                               SM_CLEAN_OUT)) ? SEL_EXCEPTS : SEL_SENDS));
+       }
+       /* Set dirty read if sm.dirty_in is not full */
+       if(t->dirty_read != -1) {
+               selector_add_raw_fd(s, t->dirty_read,
+                       (buffer_full(state_machine_get_buffer(&t->sm,
+                               SM_DIRTY_IN)) ? SEL_EXCEPTS : SEL_READS));
+       }
+       /* Set dirty send if sm.dirty_out is not empty */
+       if(t->dirty_send != -1) {
+               selector_add_raw_fd(s, t->dirty_send,
+                       (buffer_empty(state_machine_get_buffer(&t->sm,
+                               SM_DIRTY_OUT)) ? SEL_EXCEPTS : SEL_SENDS));
+       }
+}
+
+static int selector_select(tunala_selector_t *selector)
+{
+       memcpy(&selector->last_selected, &selector->next_select,
+                       sizeof(select_sets_t));
+       selector_sets_init(&selector->next_select);
+       return select(selector->last_selected.max,
+                       &selector->last_selected.reads,
+                       &selector->last_selected.sends,
+                       &selector->last_selected.excepts, NULL);
+}
+
+/* This returns -1 for error, 0 for no new connections, or 1 for success, in
+ * which case *newfd is populated. */
+static int selector_get_listener(tunala_selector_t *selector, int fd, int *newfd)
+{
+       if(FD_ISSET(fd, &selector->last_selected.excepts))
+               return -1;
+       if(!FD_ISSET(fd, &selector->last_selected.reads))
+               return 0;
+       if((*newfd = ip_accept_connection(fd)) == -1)
+               return -1;
+       return 1;
+}
+
+/************************/
+/* "Tunala" world stuff */
+/************************/
+
+static int tunala_world_make_room(tunala_world_t *world)
+{
+       unsigned int newsize;
+       tunala_item_t *newarray;
+
+       if(world->tunnels_used < world->tunnels_size)
+               return 1;
+       newsize = (world->tunnels_size == 0 ? 16 :
+                       ((world->tunnels_size * 3) / 2));
+       if((newarray = malloc(newsize * sizeof(tunala_item_t))) == NULL)
+               return 0;
+       memset(newarray, 0, newsize * sizeof(tunala_item_t));
+       if(world->tunnels_used > 0)
+               memcpy(newarray, world->tunnels,
+                       world->tunnels_used * sizeof(tunala_item_t));
+       if(world->tunnels_size > 0)
+               free(world->tunnels);
+       /* migrate */
+       world->tunnels = newarray;
+       world->tunnels_size = newsize;
+       return 1;
+}
+
+static int tunala_world_new_item(tunala_world_t *world, int fd,
+               const unsigned char *ip, unsigned short port)
+{
+       tunala_item_t *item;
+       int newfd;
+
+       if(!tunala_world_make_room(world))
+               return 0;
+       item = world->tunnels + (world->tunnels_used++);
+       state_machine_init(&item->sm);
+       item->clean_read = item->clean_send =
+               item->dirty_read = item->dirty_send = -1;
+       if((newfd = ip_create_connection_split(ip, port)) == -1)
+               goto err;
+       /* Which way round? If we're a server, "fd" is the dirty side and the
+        * connection we open is the clean one. For a client, it's the other way
+        * around. */
+       if(world->server_mode) {
+               item->dirty_read = item->dirty_send = fd;
+               item->clean_read = item->clean_send = newfd;
+       } else {
+               item->clean_read = item->clean_send = fd;
+               item->dirty_read = item->dirty_send = newfd;
+       }
+       state_machine_set_SSL(&item->sm, SSL_new(world->ssl_ctx),
+                       world->server_mode);
+       return 1;
+err:
+       state_machine_close(&item->sm);
+       return 0;
+
+}
+
+static void tunala_world_del_item(tunala_world_t *world, unsigned int idx)
+{
+       tunala_item_t *item = world->tunnels + idx;
+       if(item->clean_read != -1)
+               close(item->clean_read);
+       if(item->clean_send != item->clean_read)
+               close(item->clean_send);
+       item->clean_read = item->clean_send = -1;
+       if(item->dirty_read != -1)
+               close(item->dirty_read);
+       if(item->dirty_send != item->dirty_read)
+               close(item->dirty_send);
+       item->dirty_read = item->dirty_send = -1;
+       state_machine_close(&item->sm);
+       /* OK, now we fix the item array */
+       if(idx + 1 < world->tunnels_used)
+               /* We need to scroll entries to the left */
+               memmove(world->tunnels + idx,
+                               world->tunnels + (idx + 1),
+                               (world->tunnels_used - (idx + 1)) *
+                                       sizeof(tunala_item_t));
+       world->tunnels_used--;
+}
+
+static int tunala_item_io(tunala_selector_t *selector, tunala_item_t *item)
+{
+       int c_r, c_s, d_r, d_s; /* Four boolean flags */
+
+       /* Take ourselves out of the gene-pool if there was an except */
+       if((item->clean_read != -1) && FD_ISSET(item->clean_read,
+                               &selector->last_selected.excepts))
+               return 0;
+       if((item->clean_send != -1) && FD_ISSET(item->clean_send,
+                               &selector->last_selected.excepts))
+               return 0;
+       if((item->dirty_read != -1) && FD_ISSET(item->dirty_read,
+                               &selector->last_selected.excepts))
+               return 0;
+       if((item->dirty_send != -1) && FD_ISSET(item->dirty_send,
+                               &selector->last_selected.excepts))
+               return 0;
+       /* Grab our 4 IO flags */
+       c_r = c_s = d_r = d_s = 0;
+       if(item->clean_read != -1)
+               c_r = FD_ISSET(item->clean_read, &selector->last_selected.reads);
+       if(item->clean_send != -1)
+               c_s = FD_ISSET(item->clean_send, &selector->last_selected.sends);
+       if(item->dirty_read != -1)
+               d_r = FD_ISSET(item->dirty_read, &selector->last_selected.reads);
+       if(item->dirty_send != -1)
+               d_s = FD_ISSET(item->dirty_send, &selector->last_selected.sends);
+       /* If no IO has happened for us, skip needless data looping */
+       if(!c_r && !c_s && !d_r && !d_s)
+               return 1;
+       if(c_r)
+               c_r = (buffer_from_fd(state_machine_get_buffer(&item->sm,
+                               SM_CLEAN_IN), item->clean_read) <= 0);
+       if(c_s)
+               c_s = (buffer_to_fd(state_machine_get_buffer(&item->sm,
+                               SM_CLEAN_OUT), item->clean_send) <= 0);
+       if(d_r)
+               d_r = (buffer_from_fd(state_machine_get_buffer(&item->sm,
+                               SM_DIRTY_IN), item->dirty_read) <= 0);
+       if(d_s)
+               d_s = (buffer_to_fd(state_machine_get_buffer(&item->sm,
+                               SM_DIRTY_OUT), item->dirty_send) <= 0);
+       /* If any of the flags is non-zero, that means they need closing */
+       if(c_r) {
+               close(item->clean_read);
+               if(item->clean_send == item->clean_read)
+                       item->clean_send = -1;
+               item->clean_read = -1;
+       }
+       if(c_s) {
+               close(item->clean_send);
+               if(item->clean_send == item->clean_read)
+                       item->clean_read = -1;
+               item->clean_send = -1;
+       }
+       if(d_r) {
+               close(item->dirty_read);
+               if(item->dirty_send == item->dirty_read)
+                       item->dirty_send = -1;
+               item->dirty_read = -1;
+       }
+       if(d_s) {
+               close(item->dirty_send);
+               if(item->dirty_send == item->dirty_read)
+                       item->dirty_read = -1;
+               item->dirty_send = -1;
+       }
+       /* This function name is attributed to the term donated by David
+        * Schwartz on openssl-dev, message-ID:
+        * <NCBBLIEPOCNJOAEKBEAKEEDGLIAA.davids@webmaster.com>. :-) */
+       if(!state_machine_churn(&item->sm))
+               /* If the SSL closes, it will also zero-out the _in buffers
+                * and will in future process just outgoing data. As and
+                * when the outgoing data has gone, it will return zero
+                * here to tell us to bail out. */
+               return 0;
+       /* Otherwise, we return zero if both sides are dead. */
+       if(((item->clean_read == -1) || (item->clean_send == -1)) &&
+                       ((item->dirty_read == -1) || (item->dirty_send == -1)))
+               return 0;
+       /* If only one side closed, notify the SSL of this so it can take
+        * appropriate action. */
+       if((item->clean_read == -1) || (item->clean_send == -1)) {
+               if(!state_machine_close_clean(&item->sm))
+                       return 0;
+       }
+       if((item->dirty_read == -1) || (item->dirty_send == -1)) {
+               if(state_machine_close_dirty(&item->sm))
+                       return 0;
+       }
+       return 1;
+}
+
diff --git a/demos/tunala/tunala.h b/demos/tunala/tunala.h
new file mode 100644 (file)
index 0000000..7ad012b
--- /dev/null
@@ -0,0 +1,146 @@
+/* Tunala ("Tunneler with a New Zealand accent")
+ *
+ * Written by Geoff Thorpe, but endorsed/supported by noone. Please use this is
+ * if it's useful or informative to you, but it's only here as a scratchpad for
+ * ideas about how you might (or might not) program with OpenSSL. If you deploy
+ * this is in a mission-critical environment, and have not read, understood,
+ * audited, and modified this code to your satisfaction, and the result is that
+ * all hell breaks loose and you are looking for a new employer, then it proves
+ * nothing except perhaps that Darwinism is alive and well. Let's just say, *I*
+ * don't use this in a mission-critical environment, so it would be stupid for
+ * anyone to assume that it is solid and/or tested enough when even its author
+ * doesn't place that much trust in it. You have been warned.
+ *
+ * With thanks to Cryptographic Appliances, Inc.
+ */
+
+#ifndef _TUNALA_H
+#define _TUNALA_H
+
+#ifndef NO_SYSTEM_H
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <signal.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#endif /* !defined(NO_SYSTEM_H) */
+
+#ifndef NO_OPENSSL
+#include <openssl/err.h>
+#include <openssl/engine.h>
+#include <openssl/ssl.h>
+#endif /* !defined(NO_OPENSSL) */
+
+#ifndef NO_BUFFER
+/* This is the generic "buffer" type that is used when feeding the
+ * state-machine. It's basically a FIFO with respect to the "adddata" &
+ * "takedata" type functions that operate on it. */
+#define MAX_DATA_SIZE 16384
+typedef struct _buffer_t {
+       unsigned char data[MAX_DATA_SIZE];
+       unsigned int used;
+} buffer_t;
+
+/* Initialise a buffer structure before use */
+void buffer_init(buffer_t *buf);
+/* Cleanup a buffer structure - presently not needed, but if buffer_t is
+ * converted to using dynamic allocation, this would be required - so should be
+ * called to protect against an explosion of memory leaks later if the change is
+ * made. */
+void buffer_close(buffer_t *buf);
+
+/* Basic functions to manipulate buffers */
+
+unsigned int buffer_used(buffer_t *buf); /* How much data in the buffer */
+unsigned int buffer_unused(buffer_t *buf); /* How much space in the buffer */
+int buffer_full(buffer_t *buf); /* Boolean, is it full? */
+int buffer_notfull(buffer_t *buf); /* Boolean, is it not full? */
+int buffer_empty(buffer_t *buf); /* Boolean, is it empty? */
+int buffer_notempty(buffer_t *buf); /* Boolean, is it not empty? */
+
+/* Add data to the tail of the buffer, returns the amount that was actually
+ * added (so, you need to check if return value is less than size) */
+unsigned int buffer_adddata(buffer_t *buf, const unsigned char *ptr,
+               unsigned int size);
+
+/* Take data from the front of the buffer (and scroll the rest forward). If
+ * "ptr" is NULL, this just removes data off the front of the buffer. Return
+ * value is the amount actually removed (can be less than size if the buffer has
+ * too little data). */
+unsigned int buffer_takedata(buffer_t *buf, unsigned char *ptr,
+               unsigned int size);
+
+/* Flushes as much data as possible out of the "from" buffer into the "to"
+ * buffer. Return value is the amount moved. The amount moved can be restricted
+ * to a maximum by specifying "cap" - setting it to -1 means no limit. */
+unsigned int buffer_tobuffer(buffer_t *to, buffer_t *from, int cap);
+
+#ifndef NO_IP
+/* Read or write between a file-descriptor and a buffer */
+int buffer_from_fd(buffer_t *buf, int fd);
+int buffer_to_fd(buffer_t *buf, int fd);
+#endif /* !defined(NO_IP) */
+
+#ifndef NO_OPENSSL
+/* Read or write between an SSL or BIO and a buffer */
+void buffer_from_SSL(buffer_t *buf, SSL *ssl);
+void buffer_to_SSL(buffer_t *buf, SSL *ssl);
+void buffer_from_BIO(buffer_t *buf, BIO *bio);
+void buffer_to_BIO(buffer_t *buf, BIO *bio);
+#endif /* !defined(NO_OPENSSL) */
+#endif /* !defined(NO_BUFFER) */
+
+#ifndef NO_TUNALA
+#ifdef NO_BUFFER
+#error "TUNALA section of tunala.h requires BUFFER support"
+#endif
+typedef struct _state_machine_t {
+       SSL *ssl;
+       BIO *bio_intossl;
+       BIO *bio_fromssl;
+       buffer_t clean_in, clean_out;
+       buffer_t dirty_in, dirty_out;
+} state_machine_t;
+typedef enum {
+       SM_CLEAN_IN, SM_CLEAN_OUT,
+       SM_DIRTY_IN, SM_DIRTY_OUT
+} sm_buffer_t;
+void state_machine_init(state_machine_t *machine);
+void state_machine_close(state_machine_t *machine);
+buffer_t *state_machine_get_buffer(state_machine_t *machine, sm_buffer_t type);
+SSL *state_machine_get_SSL(state_machine_t *machine);
+void state_machine_set_SSL(state_machine_t *machine, SSL *ssl, int is_server);
+/* Performs the data-IO loop and returns zero if the machine should close */
+int state_machine_churn(state_machine_t *machine);
+/* Is used to handle closing conditions - namely when one side of the tunnel has
+ * closed but the other should finish flushing. */
+int state_machine_close_clean(state_machine_t *machine);
+int state_machine_close_dirty(state_machine_t *machine);
+#endif /* !defined(NO_TUNALA) */
+
+#ifndef NO_IP
+/* Initialise anything related to the networking. This includes blocking pesky
+ * SIGPIPE signals. */
+int ip_initialise(void);
+/* ip is the 4-byte ip address (eg. 127.0.0.1 is {0x7F,0x00,0x00,0x01}), port is
+ * the port to listen on (host byte order), and the return value is the
+ * file-descriptor or -1 on error. */
+int ip_create_listener_split(const unsigned char *ip, unsigned short port);
+/* Same semantics as above. */
+int ip_create_connection_split(const unsigned char *ip, unsigned short port);
+/* Converts a string into the ip/port before calling the above */
+int ip_create_listener(const char *address);
+int ip_create_connection(const char *address);
+/* Just does a string conversion on its own. NB: If accept_all_ip is non-zero,
+ * then the address string could be just a port. Ie. it's suitable for a
+ * listening address but not a connecting address. */
+int ip_parse_address(const char *address, unsigned char **parsed_ip,
+               unsigned short *port, int accept_all_ip);
+/* Accepts an incoming connection through the listener. Assumes selects and
+ * what-not have deemed it an appropriate thing to do. */
+int ip_accept_connection(int listen_fd);
+#endif /* !defined(NO_IP) */
+
+#endif /* !defined(_TUNALA_H) */