This is a demo that performs SSL tunneling (client and/or server) and is
built using an abstracted state machine with a non-blocking IP wrapper around it. README will follow in the next commit.
This commit is contained in:
parent
14c6d27d63
commit
d1855cc7af
85
demos/tunala/A-client.pem
Normal file
85
demos/tunala/A-client.pem
Normal file
@ -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-----
|
85
demos/tunala/A-server.pem
Normal file
85
demos/tunala/A-server.pem
Normal file
@ -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-----
|
24
demos/tunala/CA.pem
Normal file
24
demos/tunala/CA.pem
Normal file
@ -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-----
|
40
demos/tunala/Makefile
Normal file
40
demos/tunala/Makefile
Normal file
@ -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
|
146
demos/tunala/buffer.c
Normal file
146
demos/tunala/buffer.c
Normal file
@ -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) */
|
147
demos/tunala/ip.c
Normal file
147
demos/tunala/ip.c
Normal file
@ -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) */
|
||||
|
149
demos/tunala/sm.c
Normal file
149
demos/tunala/sm.c
Normal file
@ -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) */
|
||||
|
733
demos/tunala/tunala.c
Normal file
733
demos/tunala/tunala.c
Normal file
@ -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;
|
||||
}
|
||||
|
146
demos/tunala/tunala.h
Normal file
146
demos/tunala/tunala.h
Normal file
@ -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) */
|
Loading…
Reference in New Issue
Block a user