hare

[hare] The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

registry.ha (3642B)


      1 // SPDX-License-Identifier: MPL-2.0
      2 // (c) Hare authors <https://harelang.org>
      3 
      4 use net;
      5 use net::dns;
      6 use unix::hosts;
      7 
      8 // Returned if the address parameter was invalid, for example if it specifies an
      9 // invalid port number.
     10 export type invalid_address = !void;
     11 
     12 // Returned if the service parameter does not name a service known to the
     13 // system.
     14 export type unknown_service = !void;
     15 
     16 // Errors which can occur from dial.
     17 export type error = !(invalid_address | unknown_service | net::error | dns::error
     18 	| hosts::error);
     19 
     20 // Converts an [[error]] to a human-readable string. The result may be
     21 // statically allocated.
     22 export fn strerror(err: error) const str = {
     23 	// TODO: These could be better
     24 	match (err) {
     25 	case invalid_address =>
     26 		return "Attempted to dial an invalid address";
     27 	case unknown_service =>
     28 		return "Unknown service";
     29 	case let err: net::error =>
     30 		return net::strerror(err);
     31 	case let err: dns::error =>
     32 		return dns::strerror(err);
     33 	case let err: hosts::error =>
     34 		return hosts::strerror(err);
     35 	};
     36 };
     37 
     38 // A dialer is a function which implements dial for a specific protocol.
     39 export type dialer = fn(addr: str, service: str) (net::socket | error);
     40 
     41 type protocol = struct {
     42 	name: str,
     43 	dial: *dialer,
     44 };
     45 
     46 type service = struct {
     47 	proto: str,
     48 	name: str,
     49 	alias: []str,
     50 	port: u16,
     51 };
     52 
     53 let default_protocols: [_]protocol = [
     54 	protocol { name = "tcp", dial = &dial_tcp },
     55 	protocol { name = "udp", dial = &dial_udp },
     56 ];
     57 
     58 let default_services: [_]service = [
     59 	service { proto = "tcp", name = "ssh", alias = [], port = 22 },
     60 	service { proto = "tcp", name = "smtp", alias = ["mail"], port = 25 },
     61 	service { proto = "tcp", name = "domain", alias = ["dns"], port = 53 },
     62 	service { proto = "tcp", name = "http", alias = ["www"], port = 80 },
     63 	service { proto = "tcp", name = "imap2", alias = ["imap"], port = 143 },
     64 	service { proto = "tcp", name = "https", alias = [], port = 443 },
     65 	service { proto = "tcp", name = "submission", alias = [], port = 587 },
     66 	service { proto = "tcp", name = "imaps", alias = [], port = 993 },
     67 	service { proto = "udp", name = "domain", alias = ["dns"], port = 53 },
     68 	service { proto = "udp", name = "ntp", alias = [], port = 123 },
     69 ];
     70 
     71 let protocols: []protocol = [];
     72 let services: []service = [];
     73 
     74 @fini fn fini() void = {
     75 	free(protocols);
     76 	free(services);
     77 };
     78 
     79 // Registers a new transport-level protocol (e.g. TCP) with the dialer. The name
     80 // should be statically allocated.
     81 export fn registerproto(name: str, dial: *dialer) void = {
     82 	append(protocols, protocol {
     83 		name = name,
     84 		dial = dial,
     85 	});
     86 };
     87 
     88 // Registers a new application-level service (e.g. SSH) with the dialer. Note
     89 // that the purpose of services is simply to establish the default outgoing
     90 // port for TCP and UDP connections. The name and alias list should be
     91 // statically allocated.
     92 export fn registersvc(
     93 	proto: str,
     94 	name: str,
     95 	alias: []str,
     96 	port: u16,
     97 ) void = {
     98 	append(services, service {
     99 		proto = proto,
    100 		name = name,
    101 		alias = alias,
    102 		port = port,
    103 	});
    104 };
    105 
    106 fn lookup_service(proto: str, service: str) (u16 | void) = {
    107 	for (let i = 0z; i < len(default_services); i += 1) {
    108 		const serv = &default_services[i];
    109 		if (service_match(serv, proto, service)) {
    110 			return serv.port;
    111 		};
    112 	};
    113 
    114 	for (let i = 0z; i < len(services); i += 1) {
    115 		const serv = &services[i];
    116 		if (service_match(serv, proto, service)) {
    117 			return serv.port;
    118 		};
    119 	};
    120 };
    121 
    122 fn service_match(candidate: *service, proto: str, service: str) bool = {
    123 	if (candidate.name == service) {
    124 		return true;
    125 	};
    126 	for (let j = 0z; j < len(candidate.alias); j += 1) {
    127 		if (candidate.alias[j] == service) {
    128 			return true;
    129 		};
    130 	};
    131 	return false;
    132 };