static AthenaTransportLink *
_TemplateOpen(AthenaTransportLinkModule *athenaTransportLinkModule, PARCURI *connectionURI)
{
    // Parse the URI contents to determine the link specific parameters
    const char *authorityString = parcURI_GetAuthority(connectionURI);
    if (authorityString == NULL) {
        parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                      "Unable to parse connection authority %s", authorityString);
        errno = EINVAL;
        return NULL;
    }

    //
    // This template link module doesn't use the authority fields.
    // The access methods are here for use by derived link modules, if needed.
    //
    PARCURIAuthority *authority = parcURIAuthority_Parse(authorityString);
    //const char *URIAddress = parcURIAuthority_GetHostName(authority);
    //in_port_t port = parcURIAuthority_GetPort(authority);
    parcURIAuthority_Release(&authority);

    int forceLocal = 0;
    char specifiedLinkName[MAXPATHLEN] = { 0 };
    const char *linkName = NULL;

    // Parse connection segment parameters, Name and Local
    PARCURIPath *remainder = parcURI_GetPath(connectionURI);
    size_t segments = parcURIPath_Count(remainder);
    for (int i = 0; i < segments; i++) {
        PARCURISegment *segment = parcURIPath_Get(remainder, i);
        const char *token = parcURISegment_ToString(segment);

        if (strncasecmp(token, LINK_NAME_SPECIFIER, strlen(LINK_NAME_SPECIFIER)) == 0) {
            if (_parseLinkName(token, specifiedLinkName) != 0) {
                parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                              "Improper connection name specification (%s)", token);
                parcMemory_Deallocate(&token);
                errno = EINVAL;
                return NULL;
            }
            linkName = specifiedLinkName;
            parcMemory_Deallocate(&token);
            continue;
        }

        if (strncasecmp(token, LOCAL_LINK_FLAG, strlen(LOCAL_LINK_FLAG)) == 0) {
            forceLocal = _parseLocalFlag(token);
            if (forceLocal == 0) {
                parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                              "Improper local specification (%s)", token);
                parcMemory_Deallocate(&token);
                errno = EINVAL;
                return NULL;
            }
            parcMemory_Deallocate(&token);
            continue;
        }

        parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                      "Unknown connection parameter (%s)", token);
        parcMemory_Deallocate(&token);
        errno = EINVAL;
        return NULL;
    }

    _TemplateLinkData *linkData = _TemplateLinkData_Create();

    const char *derivedLinkName = _createNameFromLinkData(linkData);

    if (linkName == NULL) {
        linkName = derivedLinkName;
    }

    AthenaTransportLink *athenaTransportLink = athenaTransportLink_Create(linkName,
                                                                          _TemplateSend,
                                                                          _TemplateReceive,
                                                                          _TemplateClose);
    if (athenaTransportLink == NULL) {
        parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                      "athenaTransportLink_Create failed");
        parcMemory_Deallocate(&derivedLinkName);
        _TemplateLinkData_Destroy(&linkData);
        return athenaTransportLink;
    }

    athenaTransportLink_SetPrivateData(athenaTransportLink, linkData);
    athenaTransportLink_SetEvent(athenaTransportLink, AthenaTransportLinkEvent_Send);

    parcLog_Info(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                 "new link established: Name=\"%s\" (%s)", linkName, derivedLinkName);

    parcMemory_Deallocate(&derivedLinkName);

    // forced IsLocal/IsNotLocal, mainly for testing
    if (athenaTransportLink && forceLocal) {
        athenaTransportLink_ForceLocal(athenaTransportLink, forceLocal);
    }

    return athenaTransportLink;
}
static AthenaTransportLink *
_UDPOpen(AthenaTransportLinkModule *athenaTransportLinkModule, PARCURI *connectionURI)
{
    AthenaTransportLink *result = 0;

    const char *authorityString = parcURI_GetAuthority(connectionURI);
    if (authorityString == NULL) {
        parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                      "Unable to parse connection authority %s", authorityString);
        errno = EINVAL;
        return NULL;
    }
    PARCURIAuthority *authority = parcURIAuthority_Parse(authorityString);
    const char *URIAddress = parcURIAuthority_GetHostName(authority);
    in_port_t port = parcURIAuthority_GetPort(authority);

    // Normalize the provided hostname
    struct sockaddr_in *addr = (struct sockaddr_in *) parcNetwork_SockAddress(URIAddress, port);
    char *address = inet_ntoa(addr->sin_addr);
    parcMemory_Deallocate(&addr);

    parcURIAuthority_Release(&authority);

    if (address == NULL) {
        parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                      "Unable to lookup hostname %s", address);
        errno = EINVAL;
        return NULL;
    }
    if (port == 0) {
        parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                      "Invalid address specification, port == 0");
        errno = EINVAL;
        return NULL;
    }

    bool listener = false;
    char name[MAXPATHLEN] = { 0 };
    char srcAddress[NI_MAXHOST] = "0.0.0.0";
    size_t mtu = 0;
    uint16_t srcPort = 0;
    char localFlag[MAXPATHLEN] = { 0 };
    int forceLocal = 0;
    char *linkName = NULL;

    PARCURIPath *remainder = parcURI_GetPath(connectionURI);
    size_t segments = parcURIPath_Count(remainder);
    for (int i = 0; i < segments; i++) {
        PARCURISegment *segment = parcURIPath_Get(remainder, i);
        const char *token = parcURISegment_ToString(segment);

        if (strcasecmp(token, UDP_LISTENER_FLAG) == 0) {
            listener = true;
            parcMemory_Deallocate(&token);
            continue;
        }

        if (strncasecmp(token, SRC_LINK_SPECIFIER, strlen(SRC_LINK_SPECIFIER)) == 0) {
            if (sscanf(token, "%*[^%%]%%3D%[^%%]%%3A%hd", srcAddress, &srcPort) != 2) {
                parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                              "Improper connection source specification (%s)", token);
                parcMemory_Deallocate(&token);
                errno = EINVAL;
                return NULL;
            }
            // Normalize the provided hostname
            struct sockaddr_in *addr = (struct sockaddr_in *) parcNetwork_SockAddress(srcAddress, srcPort);
            char *hostname = inet_ntoa(addr->sin_addr);
            parcMemory_Deallocate(&addr);

            memcpy(srcAddress, hostname, strlen(hostname) + 1);
            parcMemory_Deallocate(&token);
            continue;
        }

        if (strncasecmp(token, LINK_MTU_SIZE, strlen(LINK_MTU_SIZE)) == 0) {
            if (sscanf(token, "%*[^%%]%%3D%zd", &mtu) != 1) {
                parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                              "Improper MTU specification (%s)", token);
                parcMemory_Deallocate(&token);
                errno = EINVAL;
                return NULL;
            }
            parcMemory_Deallocate(&token);
            continue;
        }

        if (strncasecmp(token, LINK_NAME_SPECIFIER, strlen(LINK_NAME_SPECIFIER)) == 0) {
            if (sscanf(token, "%*[^%%]%%3D%s", name) != 1) {
                parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                              "Improper connection name specification (%s)", token);
                parcMemory_Deallocate(&token);
                errno = EINVAL;
                return NULL;
            }
            linkName = name;
            parcMemory_Deallocate(&token);
            continue;
        }

        if (strncasecmp(token, LOCAL_LINK_FLAG, strlen(LOCAL_LINK_FLAG)) == 0) {
            if (sscanf(token, "%*[^%%]%%3D%s", localFlag) != 1) {
                parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                              "Improper local specification (%s)", token);
                parcMemory_Deallocate(&token);
                errno = EINVAL;
                return NULL;
            }
            if (strncasecmp(localFlag, "false", strlen("false")) == 0) {
                forceLocal = AthenaTransportLink_ForcedNonLocal;
            } else if (strncasecmp(localFlag, "true", strlen("true")) == 0) {
                forceLocal = AthenaTransportLink_ForcedLocal;
            } else {
                parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                              "Improper local state specification (%s)", token);
                parcMemory_Deallocate(&token);
                errno = EINVAL;
                return NULL;
            }
            parcMemory_Deallocate(&token);
            continue;
        }

        parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                      "Unknown connection parameter (%s)", token);
        parcMemory_Deallocate(&token);
        errno = EINVAL;
        return NULL;
    }

    struct sockaddr_in *destination = parcNetwork_SockInet4Address(address, port);
    struct sockaddr_in *source = parcNetwork_SockInet4Address(srcAddress, srcPort);

    if (listener) {
        result = _UDPOpenListener(athenaTransportLinkModule, linkName, destination, mtu);
    } else {
        result = _UDPOpenConnection(athenaTransportLinkModule, linkName, source, destination, mtu);
    }

    parcMemory_Deallocate(&destination);
    parcMemory_Deallocate(&source);

    // forced IsLocal/IsNotLocal, mainly for testing
    if (result && forceLocal) {
        athenaTransportLink_ForceLocal(result, forceLocal);
    }

    return result;
}
static AthenaTransportLink *
_ETHOpen(AthenaTransportLinkModule *athenaTransportLinkModule, PARCURI *connectionURI)
{
    AthenaTransportLink *result = 0;
    char device[NI_MAXHOST];

    const char *authorityString = parcURI_GetAuthority(connectionURI);
    if (authorityString == NULL) {
        parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                      "Unable to parse connection authority %s", authorityString);
        errno = EINVAL;
        return NULL;
    }
    PARCURIAuthority *authority = parcURIAuthority_Parse(authorityString);
    const char *URIHostname = parcURIAuthority_GetHostName(authority);
    strcpy(device, URIHostname);

    bool srcMACSpecified = false;
    struct ether_addr srcMAC = { { 0 } };
    struct ether_addr destMAC = { { 0 } };
    if (_parseAddress(authorityString, &destMAC) != 0) {
        parcURIAuthority_Release(&authority);
        errno = EINVAL;
        return NULL;
    }
    parcURIAuthority_Release(&authority);

    bool isListener = false;
    char *linkName = NULL;
    char specifiedLinkName[MAXPATHLEN] = { 0 };
    size_t mtu = 0;
    int forceLocal = 0;

    PARCURIPath *remainder = parcURI_GetPath(connectionURI);
    size_t segments = parcURIPath_Count(remainder);
    for (int i = 0; i < segments; i++) {
        PARCURISegment *segment = parcURIPath_Get(remainder, i);
        const char *token = parcURISegment_ToString(segment);

        if (strcasecmp(token, ETH_LISTENER_FLAG) == 0) {
            // Packet source for listener is destination parameter, unless told otherwise
            if (srcMACSpecified == false) {
                memcpy(&srcMAC, &destMAC, sizeof(struct ether_addr));
            }
            isListener = true;
            parcMemory_Deallocate(&token);
            continue;
        }

        if (strncasecmp(token, SRC_LINK_SPECIFIER, strlen(SRC_LINK_SPECIFIER)) == 0) {
            if (_parseSrc(athenaTransportLinkModule, token, &srcMAC) != 0) {
                parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                              "Improper connection source specification (%s)", token);
                parcMemory_Deallocate(&token);
                return NULL;
            }
            srcMACSpecified = true;
            parcMemory_Deallocate(&token);
            continue;
        }

        if (strncasecmp(token, LINK_MTU_SIZE, strlen(LINK_MTU_SIZE)) == 0) {
            if (_parseMTU(token, &mtu) == -1) {
                parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                              "Improper MTU specification (%s)", token);
                parcMemory_Deallocate(&token);
                errno = EINVAL;
                return NULL;
            }
            parcMemory_Deallocate(&token);
            continue;
        }

        if (strncasecmp(token, LINK_NAME_SPECIFIER, strlen(LINK_NAME_SPECIFIER)) == 0) {
            if (_parseLinkName(token, specifiedLinkName) != 0) {
                parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                              "Improper connection name specification (%s)", token);
                parcMemory_Deallocate(&token);
                errno = EINVAL;
                return NULL;
            }
            linkName = specifiedLinkName;
            parcMemory_Deallocate(&token);
            continue;
        }

        if (strncasecmp(token, LOCAL_LINK_FLAG, strlen(LOCAL_LINK_FLAG)) == 0) {
            forceLocal = _parseLocalFlag(token);
            if (forceLocal == 0) {
                parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                              "Improper local specification (%s)", token);
                parcMemory_Deallocate(&token);
                errno = EINVAL;
                return NULL;
            }
            parcMemory_Deallocate(&token);
            continue;
        }

        parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                      "Unknown connection parameter (%s)", token);
        parcMemory_Deallocate(&token);
        errno = EINVAL;
        return NULL;
    }

    if (isListener) {
        result = _ETHOpenListener(athenaTransportLinkModule, linkName, device, &srcMAC, mtu);
    } else {
        if (srcMACSpecified) {
            result = _ETHOpenConnection(athenaTransportLinkModule, linkName, device, &srcMAC, &destMAC, mtu);
        } else {
            result = _ETHOpenConnection(athenaTransportLinkModule, linkName, device, NULL, &destMAC, mtu);
        }
    }

    // forced IsLocal/IsNotLocal, mainly for testing
    if (result && forceLocal) {
        athenaTransportLink_ForceLocal(result, forceLocal);
    }

    return result;
}