From 537ff39898992c3915bcf01e3840becc6750520f Mon Sep 17 00:00:00 2001 From: Jeffrey Armstrong Date: Wed, 29 Apr 2020 17:39:05 -0400 Subject: Added some very low-level network code --- gemini.prj | 29 ++++++--- jessl.f90 | 163 ++++++++++++++++++++++++++++++++++++++++++++++++ network.f90 | 201 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ test_ssl.f90 | 33 ++++++++++ 4 files changed, 418 insertions(+), 8 deletions(-) create mode 100644 jessl.f90 create mode 100644 network.f90 create mode 100644 test_ssl.f90 diff --git a/gemini.prj b/gemini.prj index 7a52da9..8306deb 100644 --- a/gemini.prj +++ b/gemini.prj @@ -4,33 +4,46 @@ "Folders":[], "Name":"+samples", "Files":[{ - "filename":".\\samples\\sample1.gmi", + "filename":"samples/sample1.gmi", + "enabled":"1" + }] + },{ + "Folders":[], + "Name":"+ssl", + "Files":[{ + "filename":"jessl.f90", + "enabled":"1" + },{ + "filename":"network.f90", + "enabled":"1" + },{ + "filename":"test_ssl.f90", "enabled":"1" }] }], "Name":"+gemini (target.exe)", "Files":[{ - "filename":".\\dumb_render.f90", + "filename":"dumb_render.f90", "enabled":"1" },{ - "filename":".\\files.f90", + "filename":"files.f90", "enabled":"1" },{ - "filename":".\\layout.f90", + "filename":"layout.f90", "enabled":"1" },{ - "filename":".\\render.f90", + "filename":"render.f90", "enabled":"1" },{ - "filename":".\\test.f90", - "enabled":"1" + "filename":"test.f90", + "enabled":"0" }] }, "Name":"gemini (target.exe)", "Options":{ "Compiler Options":{ "Fortran Flags":"", - "Link Flags":"", + "Link Flags":"-lssl -lcrypto -lpthread -ldl", "C Flags":"" }, "Architecture":1, diff --git a/jessl.f90 b/jessl.f90 new file mode 100644 index 0000000..a27803e --- /dev/null +++ b/jessl.f90 @@ -0,0 +1,163 @@ +! Just Enough SSL... + +module jessl +implicit none + + interface + + subroutine library_init() bind(c, name="SSL_library_init") + end subroutine library_init + + subroutine add_ssl_algorithms() bind(c, name="SSLeay_add_ssl_algorithms") + end subroutine add_ssl_algorithms + + subroutine load_error_strings() bind(c, name="SSL_load_error_strings") + end subroutine load_error_strings + + function tls_v1_3_client_method() bind(c, name="TLSv1_2_client_method") + use iso_c_binding + type(c_ptr)::tls_v1_3_client_method + end function tls_v1_3_client_method + + function ctx_new(meth) bind(c, name="SSL_CTX_new") + use iso_c_binding + type(c_ptr)::ctx_new + type(c_ptr), value::meth + end function ctx_new + + function ssl_new(ctx) bind(c, name="SSL_new") + use iso_c_binding + type(c_ptr)::ssl_new + type(c_ptr), value::ctx + end function ssl_new + + function get_fd(ssl) bind(c, name="SSL_get_fd") + use iso_c_binding + integer(kind=c_int)::get_fd + type(c_ptr), value::ssl + end function get_fd + + function set_fd(ssl, fd) bind(c, name="SSL_set_fd") + use iso_c_binding + integer(kind=c_int)::set_fd + integer(kind=c_int), value::fd + type(c_ptr), value::ssl + end function set_fd + + function ssl_connect(ssl) bind(c, name="SSL_connect") + use iso_c_binding + integer(kind=c_int)::connect + type(c_ptr), value::ssl + end function ssl_connect + + ! Actually a macro... + !function get_cipher_c(ssl) bind(c, name="SSL_get_cipher_name") + !use iso_c_binding + !type(c_ptr)::get_cipher_c + !type(c_ptr), value::ssl + !end function get_cipher_c + + function read_c(ssl, buf, length) bind(c, name="SSL_read") + use iso_c_binding + type(c_ptr), value::ssl + character(kind=c_char), dimension(*), intent(inout)::buf + integer(kind=c_int), value::length + integer(kind=c_int)::read_c + end function read_c + + function write_c(ssl, buf, length) bind(c, name="SSL_write") + use iso_c_binding + type(c_ptr), value::ssl + character(kind=c_char), dimension(*), intent(inout)::buf + integer(kind=c_int), value::length + integer(kind=c_int)::write_c + end function write_c + + function get_error(ssl, retcode) bind(c, name="SSL_get_error") + use iso_c_binding + type(c_ptr), value::ssl + integer(kind=c_int), value::retcode + integer(kind=c_int)::get_error + end function get_error + + end interface + +contains + + function ssl_read(ssl, buf) + use iso_c_binding + implicit none + + type(c_ptr)::ssl + character, dimension(:), intent(inout)::buf + integer::ssl_read + + character(kind=c_char), dimension(:), allocatable::cbuf + + allocate(cbuf(size(buf))) + + ssl_read = read_c(ssl, cbuf, size(buf)) + buf = cbuf + + deallocate(cbuf) + + end function ssl_read + + function ssl_write(ssl, buf) + use iso_c_binding + implicit none + + type(c_ptr)::ssl + character, dimension(:), intent(in)::buf + integer::ssl_write + + character(kind=c_char), dimension(:), allocatable::cbuf + + allocate(cbuf(size(buf))) + + cbuf = buf + ssl_write = write_c(ssl, cbuf, size(buf)) + + deallocate(cbuf) + + end function ssl_write + + function get_cipher(ssl) + use iso_c_binding + implicit none + + character(:), allocatable::get_cipher + type(c_ptr)::ssl + + type(c_ptr)::cptr + + character(kind=c_char), dimension(:), pointer::cstring + + integer::i + + cptr = c_null_ptr + if(.not. c_associated(cptr)) then + + allocate(character(len=1)::get_cipher) + get_cipher = " " + + else + + call c_f_pointer(cptr, cstring, [1]) + + i = 1 + do while(cstring(i) /= c_null_char) + i = i + 1 + end do + allocate(character(len=(i-1))::get_cipher) + + i = 1 + do while(cstring(i) /= c_null_char) + get_cipher(i:i) = cstring(i) + end do + + end if + + end function get_cipher + +end module jessl diff --git a/network.f90 b/network.f90 new file mode 100644 index 0000000..a4a3ddc --- /dev/null +++ b/network.f90 @@ -0,0 +1,201 @@ +module network +use iso_c_binding +implicit none + + integer(kind=c_int), parameter::AF_INET = 2 + integer(kind=c_int), parameter::AF_INET6 = 10 + integer(kind=c_int), parameter::AF_UNIX = 1 + + integer(kind=c_int), parameter::SOCK_STREAM = 1 + + type, bind(c) :: in_addr + integer(kind=c_int32_t)::s_addr + end type + + type, bind(c) :: sockaddr_in + integer(kind=c_short)::sin_family + integer(kind=c_int16_t)::sin_port + type(in_addr)::sin_addr + !integer(kind=c_int32_t)::s_addr + end type + + type, bind(c) :: hostent_c + type(c_ptr)::h_name !official name of host */ + type(c_ptr)::h_aliases !alias list */ + integer(kind=c_int):: h_addrtype !host address type */ + integer(kind=c_int):: h_length !length of address */ + type(c_ptr)::h_addr_list !list of addresses */ + end type + + ! Let's keep this simple... + type :: simple_hostent + character(len=:), allocatable::h_name + integer::h_addrtype + integer(kind=c_int32_t)::h_addr4 + integer(kind=c_int64_t)::h_addr6 + end type + + integer(kind=c_size_t), parameter::sockaddr_size = 56 + + interface + function socket_c(i, j, k) bind(c, name="socket") + use iso_c_binding + integer(kind=c_int), value::i, j, k + integer(kind=c_int)::socket_c + end function socket_c + + function inet_addr_c(str) bind(c, name="inet_addr") + use iso_c_binding + type(c_ptr), value::str + integer(c_int32_t)::inet_addr_c + end function inet_addr_c + + function htons(i) bind(c) + use iso_c_binding + integer(kind=c_int32_t), value::i + integer(kind=c_int32_t)::htons + end function htons + + function connect_c(sockfd, sock_addr, socklen) bind(c, name="connect") + use iso_c_binding + import::sockaddr_in + integer(kind=c_int), value::sockfd + type(c_ptr), value::sock_addr + integer(kind=c_size_t), value::socklen + integer(kind=c_int)::connect_c + end function connect_c + + function gethostbyname_c(host) bind(c, name="gethostbyname") + use iso_c_binding + type(c_ptr), value::host + type(c_ptr)::gethostbyname_c + end function gethostbyname_c + + end interface + + contains + + function socket(domain, stype, protocol) + use iso_c_binding, only: c_int + implicit none + + integer::socket + integer, intent(in)::domain, stype, protocol + + socket = socket_c(int(domain, c_int), int(stype, c_int), int(protocol, c_int)) + + end function socket + + function inet_addr(str) + use iso_c_binding + implicit none + + character(*), intent(in)::str + integer(c_int32_t)::inet_addr + + character(kind=c_char), dimension(:), allocatable, target::cstr + integer::i + + allocate(cstr(len_trim(str)+1)) + + do i=1, len_trim(str) + cstr(i) = str(i:i) + end do + cstr(len_trim(str)+1) = c_null_char + + inet_addr = inet_addr_c(c_loc(cstr)) + + deallocate(cstr) + + end function inet_addr + + function connect(sockfd, sock_addr) + use iso_c_binding + implicit none + + integer::sockfd + type(sockaddr_in), target::sock_addr + logical::connect + + !print *, c_sizeof(sock_addr) + + connect = (connect_c(int(sockfd, kind=c_int), & + c_loc(sock_addr), & + sockaddr_size) .eq. 0) + + end function connect + + function gethostbyname(host, success) result(res) + use iso_c_binding + implicit none + + character(*)::host + type(simple_hostent)::res + + type(hostent_c), pointer::cres + type(c_ptr)::callres + + logical, intent(out), optional::success + + ! To get the host to C + character(kind=c_char), dimension(:), allocatable, target::chost + integer::i + + ! To process h_name + character(kind=c_char), dimension(:), pointer::h_name + + ! To process h_addr + type(c_ptr), dimension(:), pointer::addrptr + integer(kind=c_int32_t), pointer::addr32 + integer(kind=c_int64_t), pointer::addr64 + + allocate(chost(len_trim(host)+1)) + + do i=1, len_trim(host) + chost(i) = host(i:i) + end do + chost(len_trim(host)+1) = c_null_char + + callres = gethostbyname_c(c_loc(chost)) + if(c_associated(callres)) then + call c_f_pointer(callres, cres) + + ! Extract the name + call c_f_pointer(cres%h_name, h_name, [1]) + i = 1 + do while(h_name(i) /= c_null_char) + i = i + 1 + end do + allocate(character(len=i) :: res%h_name) + i = 1 + do while(h_name(i) /= c_null_char) + res%h_name(i:i) = h_name(i) + i = i + 1 + end do + + ! And address + res%h_addr4 = 0 + res%h_addr6 = 0 + + res%h_addrtype = cres%h_addrtype + call c_f_pointer(cres%h_addr_list, addrptr, [1]) + if(res%h_addrtype == AF_INET) then + call c_f_pointer(addrptr(1), addr32) + res%h_addr4 = addr32 + else if(res%h_addrtype == AF_INET6) then + call c_f_pointer(addrptr(1), addr64) + res%h_addr6 = addr64 + end if + + if(present(success)) then + success = .TRUE. + end if + else + if(present(success)) then + success = .FALSE. + end if + end if + + end function gethostbyname + +end module network \ No newline at end of file diff --git a/test_ssl.f90 b/test_ssl.f90 new file mode 100644 index 0000000..0e169c1 --- /dev/null +++ b/test_ssl.f90 @@ -0,0 +1,33 @@ +program test_ssl +use jessl +use network +implicit none + + type(sockaddr_in), target::sa + integer::s + + type(simple_hostent)::hent + + hent = gethostbyname("google.com") + if(allocated(hent%h_name)) then + !Print *, "host: ", hent%h_name + !Print *, "addr: ", hent%h_addr4 + else + Print *, "Failure" + stop + end if + + sa%sin_family = AF_INET + sa%sin_addr%s_addr = hent%h_addr4 !inet_addr("192.168.2.90") + sa%sin_port = htons(80) + + s = socket(AF_INET, SOCK_STREAM, 0); + if(.not. connect(s, sa)) then + Print *, "Connection failed", IERRNO() + stop + end if + + Print *, "socket opened!" + + +end program test_ssl \ No newline at end of file -- cgit v1.2.3