1/*
2 * Copyright 2016, Data61
3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
4 * ABN 41 687 119 230.
5 *
6 * This software may be distributed and modified according to the terms of
7 * the BSD 2-Clause license. Note that NO WARRANTY is provided.
8 * See "LICENSE_BSD2.txt" for details.
9 *
10 * @TAG(D61_BSD)
11 */
12
13#include <refos-util/nameserv.h>
14#include <data_struct/cvector.h>
15
16/*! @file
17    @brief Name server implementation library. */
18
19static nameserv_entry_t*
20nameserv_create_entry(const char* name, seL4_CPtr anonEP)
21{
22    if (!name || !anonEP) {
23        return NULL;
24    }
25    uint32_t nameLen = strlen(name);
26    if (nameLen == 0) {
27        return NULL;
28    }
29
30    /* Allocate the nameserv entry. */
31    nameserv_entry_t* entry = malloc(sizeof(nameserv_entry_t));
32    if (!entry) {
33        printf("WARNING: nameserv_create_entry failed to malloc entry. Out of memory.\n");
34        return NULL;
35    }
36
37    /* Allocate name for the string. */
38    entry->name = malloc(sizeof(char) * (nameLen + 2));
39    if (!entry->name) {
40        printf("WARNING: nameserv_create_entry failed to malloc name str. Out of memory.\n");
41        free(entry);
42        return NULL;
43    }
44
45    /* Fill in the data. */
46    strcpy(entry->name, name);
47    entry->magic = REFOS_NAMESERV_ENTRY_MAGIC;
48    entry->anonEP = anonEP;
49
50    return entry;
51}
52
53static void
54nameserv_release_entry(nameserv_state_t *n, nameserv_entry_t* entry)
55{
56    assert(n && n->magic == REFOS_NAMESERV_MAGIC);
57    assert(entry && entry->magic == REFOS_NAMESERV_ENTRY_MAGIC);
58    assert(n->free_capability);
59
60    entry->magic = 0;
61    free(entry->name);
62    n->free_capability(entry->anonEP);
63    free(entry);
64}
65
66void
67nameserv_init(nameserv_state_t *n, void (*free_cap) (seL4_CPtr cap))
68{
69    assert(n && free_cap);
70    n->magic = REFOS_NAMESERV_MAGIC;
71    n->free_capability = free_cap;
72    cvector_init(&n->entries);
73}
74
75void
76nameserv_release(nameserv_state_t *n)
77{
78    assert(n && n->magic == REFOS_NAMESERV_MAGIC);
79    int nEntries = cvector_count(&n->entries);
80    for (int i = 0; i < nEntries; i++) {
81        nameserv_entry_t *nameEntry = (nameserv_entry_t *) cvector_get(&n->entries, i);
82        nameserv_release_entry(n, nameEntry);
83    }
84    cvector_free(&n->entries);
85    n->magic = 0;
86}
87
88int
89nameserv_add(nameserv_state_t *n, const char* name, seL4_CPtr anonEP)
90{
91    assert(n && n->magic == REFOS_NAMESERV_MAGIC);
92    if (!name || !anonEP) {
93        return EINVALIDPARAM;
94    }
95    nameserv_delete(n, name);
96    nameserv_entry_t *nameEntry = nameserv_create_entry(name, anonEP);
97    if (!nameEntry) {
98        return ENOMEM;
99    }
100    cvector_add(&n->entries, (cvector_item_t) nameEntry);
101    return ESUCCESS;
102}
103
104static int
105nameserv_find_entry_index(nameserv_state_t *n, const char* name)
106{
107    assert(n && n->magic == REFOS_NAMESERV_MAGIC);
108    if (!name) {
109        return -1;
110    }
111    int nEntries = cvector_count(&n->entries);
112    /* Loop through the list and find a matching name. */
113    for (int i = 0; i < nEntries; i++) {
114        nameserv_entry_t *nameEntry = (nameserv_entry_t *) cvector_get(&n->entries, i);
115        assert(nameEntry && nameEntry->name && nameEntry->magic == REFOS_NAMESERV_ENTRY_MAGIC);
116        if (!strcmp(nameEntry->name, name)) {
117            return i;
118        }
119    }
120    return -1;
121}
122
123void
124nameserv_delete(nameserv_state_t *n, const char* name)
125{
126    assert(n && n->magic == REFOS_NAMESERV_MAGIC);
127    int idx = nameserv_find_entry_index(n, name);
128    if (idx == -1) {
129        return;
130    }
131    nameserv_entry_t *nameEntry = (nameserv_entry_t *) cvector_get(&n->entries, idx);
132    nameserv_release_entry(n, nameEntry);
133    cvector_delete(&n->entries, idx);
134}
135
136int
137nameserv_resolve(nameserv_state_t *n, const char* path, seL4_CPtr *outAnonCap)
138{
139    assert(n && n->magic == REFOS_NAMESERV_MAGIC);
140
141    /* Return no resolve on empty paths. */
142    if (!path) {
143        return 0;
144    }
145    int pathLen = strlen(path);
146    if (pathLen <= 0) {
147        return 0;
148    }
149
150    /* Ignore any leading slashes. */
151    const char* path_ = path;
152    if (path_[0] == '/') {
153        path_++;
154    }
155
156    /* Allocate temporary buffer to store path segment. */
157    char *pathSegment = malloc(sizeof(char) * (pathLen + 2));
158    if (!pathSegment) {
159        printf("ERROR: nameserv_resolve could not allocate path segment. OOM.\n");
160        return 0;
161    }
162    int pathSegmentIndex = 0;
163
164    /* Find the next slash-separated path segment. */
165    bool segmentFound = false;
166    const char* ci;
167    for (ci = path_; *ci != '\0'; ci++) {
168        if (*ci == '/') {
169            segmentFound = true;
170            break;
171        }
172        pathSegment[pathSegmentIndex++] = *ci;
173    }
174    pathSegment[pathSegmentIndex++] = '\0';
175
176    /* If are at end of path resolvation, return our own anonymous endpoint. */
177    if (!segmentFound) {
178        return REFOS_NAMESERV_RESOLVED;
179    }
180
181    /* Otherwise, find the external anon endpoint. */
182    int idx = nameserv_find_entry_index(n, pathSegment);
183    if (idx == -1) {
184        /* Name not found. */
185        return 0;
186    }
187
188    /* External EP name resolvation succeeded. */
189    nameserv_entry_t *nameEntry = (nameserv_entry_t *) cvector_get(&n->entries, idx);
190    assert(nameEntry && nameEntry->name && nameEntry->magic == REFOS_NAMESERV_ENTRY_MAGIC);
191    if (outAnonCap) {
192        (*outAnonCap) = nameEntry->anonEP;
193    }
194    int offset = ci - path;
195    assert(offset >= 0 && offset < pathLen);
196    return offset;
197}