--- /dev/null
+/*
+ Copyright (C) by Ronnie Sahlberg <ronniesahlberg@gmail.com> 2011
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Example program using the lowlevel raw broadcast interface.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <poll.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <net/if.h>
+#include <netdb.h>
+#include "libnfs.h"
+#include "libnfs-raw.h"
+#include "libnfs-raw-mount.h"
+#include "libnfs-raw-portmap.h"
+#include "libnfs-private.h"
+
+struct nfs_list_data {
+ int status;
+ struct nfs_server_list *srvrs;
+};
+
+void free_nfs_srvr_list(struct nfs_server_list *srv)
+{
+ while (srv != NULL) {
+ struct nfs_server_list *next = srv->next;
+
+ free(srv->addr);
+ free(srv);
+ srv = next;
+ }
+}
+
+void pm_cb(struct rpc_context *rpc, int status, void *data _U_, void *private_data)
+{
+ struct nfs_list_data *srv_data = private_data;
+ struct sockaddr *sin;
+ char hostdd[16];
+ struct nfs_server_list *srvr;
+
+ if (status == RPC_STATUS_CANCEL) {
+ return;
+ }
+ if (status != 0) {
+ srv_data->status = -1;
+ return;
+ }
+
+ sin = rpc_get_recv_sockaddr(rpc);
+ if (sin == NULL) {
+ rpc_set_error(rpc, "failed to get sockaddr in CALLIT callback");
+ srv_data->status = -1;
+ return;
+ }
+
+ if (getnameinfo(sin, sizeof(struct sockaddr_in), &hostdd[0], sizeof(hostdd), NULL, 0, NI_NUMERICHOST) < 0) {
+ rpc_set_error(rpc, "getnameinfo failed in CALLIT callback");
+ srv_data->status = -1;
+ return;
+ }
+
+
+ srvr = malloc(sizeof(struct nfs_server_list));
+ if (srvr == NULL) {
+ rpc_set_error(rpc, "Malloc failed when allocating server structure");
+ srv_data->status = -1;
+ return;
+ }
+
+ srvr->addr = strdup(hostdd);
+ if (srvr->addr == NULL) {
+ rpc_set_error(rpc, "Strdup failed when allocating server structure");
+ free(srvr);
+ srv_data->status = -1;
+ return;
+ }
+
+ srvr->next = srv_data->srvrs;
+ srv_data->srvrs = srvr;
+}
+
+int main(int argc _U_, char *argv[] _U_)
+{
+ struct rpc_context *rpc;
+ struct pollfd pfd;
+ struct ifconf ifc;
+ int i, size;
+ struct timeval tv_start, tv_current;
+ struct nfs_list_data data = {0, NULL};
+ struct nfs_server_list *srvr;
+
+ rpc = rpc_init_udp_context();
+ if (rpc == NULL) {
+ printf("failed to init context\n");
+ exit(10);
+ }
+
+ if (rpc_bind_udp(rpc, "0.0.0.0", 0) < 0) {
+ printf("failed to bind to udp %s\n", rpc_get_error(rpc));
+ exit(10);
+ }
+
+
+ /* get list of all interfaces */
+ size = sizeof(struct ifreq);
+ ifc.ifc_buf = NULL;
+ ifc.ifc_len = size;
+
+ while (ifc.ifc_len == size) {
+ size *= 2;
+
+ free(ifc.ifc_buf);
+ ifc.ifc_len = size;
+ ifc.ifc_buf = malloc(size);
+ if (ioctl(rpc_get_fd(rpc), SIOCGIFCONF, (caddr_t)&ifc) < 0) {
+ printf("ioctl SIOCGIFCONF failed\n");
+ exit(10);
+ }
+ }
+
+ for (i = 0; (unsigned)i < ifc.ifc_len / sizeof(struct ifconf); i++) {
+ char bcdd[16];
+
+ if (ifc.ifc_req[i].ifr_addr.sa_family != AF_INET) {
+ continue;
+ }
+ if (ioctl(rpc_get_fd(rpc), SIOCGIFFLAGS, &ifc.ifc_req[i]) < 0) {
+ printf("ioctl DRBADDR failed\n");
+ exit(10);
+ }
+ if (!(ifc.ifc_req[i].ifr_flags & IFF_UP)) {
+ continue;
+ }
+ if (ifc.ifc_req[i].ifr_flags & IFF_LOOPBACK) {
+ continue;
+ }
+ if (!(ifc.ifc_req[i].ifr_flags & IFF_BROADCAST)) {
+ continue;
+ }
+ if (ioctl(rpc_get_fd(rpc), SIOCGIFBRDADDR, &ifc.ifc_req[i]) < 0) {
+ printf("ioctl DRBADDR failed\n");
+ exit(10);
+ }
+ if (getnameinfo(&ifc.ifc_req[i].ifr_broadaddr, sizeof(struct sockaddr_in), &bcdd[0], sizeof(bcdd), NULL, 0, NI_NUMERICHOST) < 0) {
+ printf("getnameinfo failed\n");
+ exit(10);
+ }
+ if (rpc_set_udp_destination(rpc, bcdd, 111, 1) < 0) {
+ printf("failed to set udp destination %s\n", rpc_get_error(rpc));
+ exit(10);
+ }
+
+ if (rpc_pmap_callit_async(rpc, MOUNT_PROGRAM, 2, 0, NULL, 0, pm_cb, &data) < 0) {
+ printf("Failed to set up callit function\n");
+ exit(10);
+ }
+ }
+ free(ifc.ifc_buf);
+
+ gettimeofday(&tv_start, NULL);
+ for(;;) {
+ int mpt;
+
+ pfd.fd = rpc_get_fd(rpc);
+ pfd.events = rpc_which_events(rpc);
+
+ gettimeofday(&tv_current, NULL);
+ mpt = 1000
+ - (tv_current.tv_sec *1000 + tv_current.tv_usec / 1000)
+ + (tv_start.tv_sec *1000 + tv_start.tv_usec / 1000);
+
+ if (poll(&pfd, 1, mpt) < 0) {
+ printf("Poll failed");
+ exit(10);
+ }
+ if (pfd.revents == 0) {
+ break;
+ }
+
+ if (rpc_service(rpc, pfd.revents) < 0) {
+ printf("rpc_service failed with %s\n", rpc_get_error(rpc));
+ break;
+ }
+ }
+
+ for (srvr=data.srvrs; srvr; srvr = srvr->next) {
+ printf("NFS SERVER @ %s\n", srvr->addr);
+ }
+ free_nfs_srvr_list(data.srvrs);
+
+ rpc_destroy_context(rpc);
+ rpc=NULL;
+ return 0;
+}
--- /dev/null
+/*
+ Copyright (C) by Ronnie Sahlberg <ronniesahlberg@gmail.com> 2011
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Example program showing sync interface to probe for all local servers
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <poll.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <net/if.h>
+#include <netdb.h>
+#include "libnfs.h"
+#include "libnfs-raw.h"
+#include "libnfs-raw-mount.h"
+#include "libnfs-raw-portmap.h"
+#include "libnfs-private.h"
+
+
+int main(int argc _U_, char *argv[] _U_)
+{
+ struct nfs_server_list *srvrs;
+ struct nfs_server_list *srv;
+
+ srvrs = nfs_find_local_servers();
+ for (srv=srvrs; srv; srv = srv->next) {
+ printf("NFS SERVER @ %s\n", srv->addr);
+ }
+ free_nfs_srvr_list(srvrs);
+ return 0;
+}
#define NFSFILEW "/BOOKS/Classics/foo"
#define NFSDIR "/BOOKS/Classics/"
+#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
};
-char buf[5*1024*1024];
+char buf[3*1024*1024+337];
int main(int argc _U_, char *argv[] _U_)
{
struct statvfs svfs;
exports export, tmp;
- printf("exports on server %s\n", SERVER);
export = mount_getexports(SERVER);
- tmp = export;
- while (tmp != NULL) {
- printf("Export: %s\n", tmp->ex_dir);
- tmp = tmp->ex_next;
- }
- mount_free_export_list(export);
-
+ if (export != NULL) {
+ printf("exports on server %s\n", SERVER);
+ tmp = export;
+ while (tmp != NULL) {
+ printf("Export: %s\n", tmp->ex_dir);
+ tmp = tmp->ex_next;
+ }
+
+ mount_free_export_list(export);
+ } else {
+ printf("no exports on server %s\n", SERVER);
+ }
nfs = nfs_init_context();
if (nfs == NULL) {
printf("%02x ", buf[i]&0xff);
}
printf("\n");
+ ret = nfs_read(nfs, nfsfh, sizeof(buf), buf);
+ if (ret < 0) {
+ printf("Failed to pread(%s) %s\n", NFSFILE, nfs_get_error(nfs));
+ exit(10);
+ }
+ printf("read %d bytes\n", ret);
+ ret = nfs_read(nfs, nfsfh, sizeof(buf), buf);
+ if (ret < 0) {
+ printf("Failed to pread(%s) %s\n", NFSFILE, nfs_get_error(nfs));
+ exit(10);
+ }
+ printf("read %d bytes\n", ret);
+ ret = nfs_read(nfs, nfsfh, sizeof(buf), buf);
+ if (ret < 0) {
+ printf("Failed to pread(%s) %s\n", NFSFILE, nfs_get_error(nfs));
+ exit(10);
+ }
+ printf("read %d bytes\n", ret);
+ ret = nfs_read(nfs, nfsfh, sizeof(buf), buf);
+ if (ret < 0) {
+ printf("Failed to pread(%s) %s\n", NFSFILE, nfs_get_error(nfs));
+ exit(10);
+ }
+ printf("read %d bytes\n", ret);
+ ret = nfs_read(nfs, nfsfh, sizeof(buf), buf);
+ if (ret < 0) {
+ printf("Failed to pread(%s) %s\n", NFSFILE, nfs_get_error(nfs));
+ exit(10);
+ }
+ printf("read %d bytes\n", ret);
ret = (int)nfs_lseek(nfs, nfsfh, 0, SEEK_CUR, &offset);
if (ret < 0) {
exit(10);
}
while((nfsdirent = nfs_readdir(nfs, nfsdir)) != NULL) {
- printf("Inode:%d Name:%s\n", (int)nfsdirent->inode, nfsdirent->name);
+ char *filename = NULL;
+ printf("Inode:%d Name:%s ", (int)nfsdirent->inode, nfsdirent->name);
+ asprintf(&filename, "%s/%s", NFSDIR, nfsdirent->name);
+ ret = nfs_open(nfs, filename, O_RDONLY, &nfsfh);
+ free(filename);
+ if (ret != 0) {
+ printf("Failed to open(%s) %s\n", filename, nfs_get_error(nfs));
+ exit(10);
+ }
+ ret = nfs_read(nfs, nfsfh, sizeof(buf), buf);
+ if (ret < 0) {
+ printf("Error reading file\n");
+ exit(10);
+ }
+ printf("Read %d bytes\n", ret);
+ ret = nfs_close(nfs, nfsfh);
+ if (ret < 0) {
+ printf("Failed to close(%s): %s\n", NFSFILE, nfs_get_error(nfs));
+ exit(10);
+ }
}
nfs_closedir(nfs, nfsdir);
int encodebuflen;
struct rpc_pdu *outqueue;
+ struct sockaddr_storage udp_src;
struct rpc_pdu *waitpdu;
int inpos;
int insize;
char *inbuf;
+
+ /* special fields for UDP, which can sometimes be BROADCASTed */
+ int is_udp;
+ struct sockaddr *udp_dest;
+ int is_broadcast;
};
struct rpc_pdu {
struct rpc_context *nfs_get_rpc_context(struct nfs_context *nfs);
+/* we dont want to expose UDP to normal applications/users this is private to libnfs to use exclusively for broadcast RPC */
+int rpc_bind_udp(struct rpc_context *rpc, char *addr, int port);
+int rpc_set_udp_destination(struct rpc_context *rpc, char *addr, int port, int is_broadcast);
+struct rpc_context *rpc_init_udp_context(void);
+struct sockaddr *rpc_get_recv_sockaddr(struct rpc_context *rpc);
+
int rpc_pmap_getport_async(struct rpc_context *rpc, int program, int version, rpc_cb cb, void *private_data);
+/*
+ * Call PORTMAPPER/CALLIT.
+ * Function returns
+ * 0 : The connection was initiated. Once the connection establish finishes, the callback will be invoked.
+ * <0 : An error occured when trying to set up the connection. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * RPC_STATUS_SUCCESS : We got a successful response from the portmapper daemon
+ * data is a 'pmap_call_result' pointer.
+ * RPC_STATUS_ERROR : An error occured when trying to contact the portmapper.
+ * data is the error string.
+ * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
+ * data is NULL.
+ */
+int rpc_pmap_callit_async(struct rpc_context *rpc, int program, int version, int procedure, const char *data, int datalen, rpc_cb cb, void *private_data);
/*
* MOUNT FUNCTIONS
//qqq replace later with lseek(cur, 0)
off_t nfs_get_current_offset(struct nfsfh *nfsfh);
+
+
+
+
+
+struct nfs_server_list {
+ struct nfs_server_list *next;
+ char *addr;
+};
+
+/*
+ * Sync find_local_servers(<server>)
+ * This function will probe all local networks for NFS server. This function will
+ * block for one second while awaiting for all nfs servers to respond.
+ *
+ * Function returns
+ * NULL : something failed
+ *
+ * struct nfs_server_list : a linked list of all discovered servers
+ *
+ * returned data must be freed by nfs_free_srvr_list(srv);
+ */
+struct nfs_server_list *nfs_find_local_servers(void);
+void free_nfs_srvr_list(struct nfs_server_list *srv);
}
+struct rpc_context *rpc_init_udp_context(void)
+{
+ struct rpc_context *rpc;
+
+ rpc = rpc_init_context();
+ if (rpc != NULL) {
+ rpc->is_udp = 1;
+ }
+
+ return rpc;
+}
+
void rpc_set_auth(struct rpc_context *rpc, struct AUTH *auth)
{
if (rpc->auth != NULL) {
rpc->error_string = NULL;
}
+ if (rpc->udp_dest != NULL) {
+ free(rpc->udp_dest);
+ rpc->udp_dest = NULL;
+ }
+
free(rpc);
}
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
+#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <poll.h>
+#include <net/if.h>
+#include <netdb.h>
#include "libnfs.h"
#include "libnfs-raw.h"
#include "libnfs-raw-mount.h"
return cb_data.status;
}
-void mount_getexports_cb(struct rpc_context *mount_context _U_, int status, void *data, void *private_data)
+void mount_getexports_cb(struct rpc_context *mount_context, int status, void *data, void *private_data)
{
struct sync_cb_data *cb_data = private_data;
exports export = *(exports *)data;
cb_data->status = status;
cb_data->return_data = NULL;
+ if (status != 0) {
+ rpc_set_error(mount_context, "mount/export call failed with \"%s\"", (char *)data);
+ return;
+ }
+
while (export != NULL) {
exports new_export;
}
}
+
+
+
+void free_nfs_srvr_list(struct nfs_server_list *srv)
+{
+ while (srv != NULL) {
+ struct nfs_server_list *next = srv->next;
+
+ free(srv->addr);
+ free(srv);
+ srv = next;
+ }
+}
+
+struct nfs_list_data {
+ int status;
+ struct nfs_server_list *srvrs;
+};
+
+void callit_cb(struct rpc_context *rpc, int status, void *data _U_, void *private_data)
+{
+ struct nfs_list_data *srv_data = private_data;
+ struct sockaddr *sin;
+ char hostdd[16];
+ struct nfs_server_list *srvr;
+
+ if (status == RPC_STATUS_CANCEL) {
+ return;
+ }
+ if (status != 0) {
+ srv_data->status = -1;
+ return;
+ }
+
+ sin = rpc_get_recv_sockaddr(rpc);
+ if (sin == NULL) {
+ rpc_set_error(rpc, "failed to get sockaddr in CALLIT callback");
+ srv_data->status = -1;
+ return;
+ }
+
+ if (getnameinfo(sin, sizeof(struct sockaddr_in), &hostdd[0], sizeof(hostdd), NULL, 0, NI_NUMERICHOST) < 0) {
+ rpc_set_error(rpc, "getnameinfo failed in CALLIT callback");
+ srv_data->status = -1;
+ return;
+ }
+
+
+ srvr = malloc(sizeof(struct nfs_server_list));
+ if (srvr == NULL) {
+ rpc_set_error(rpc, "Malloc failed when allocating server structure");
+ srv_data->status = -1;
+ return;
+ }
+
+ srvr->addr = strdup(hostdd);
+ if (srvr->addr == NULL) {
+ rpc_set_error(rpc, "Strdup failed when allocating server structure");
+ free(srvr);
+ srv_data->status = -1;
+ return;
+ }
+
+ srvr->next = srv_data->srvrs;
+ srv_data->srvrs = srvr;
+}
+
+struct nfs_server_list *nfs_find_local_servers(void)
+{
+ struct rpc_context *rpc;
+ struct nfs_list_data data = {0, NULL};
+ struct timeval tv_start, tv_current;
+ struct ifconf ifc;
+ int i, size;
+ struct pollfd pfd;
+
+ rpc = rpc_init_udp_context();
+ if (rpc == NULL) {
+ return NULL;
+ }
+
+ if (rpc_bind_udp(rpc, "0.0.0.0", 0) < 0) {
+ rpc_destroy_context(rpc);
+ return NULL;
+ }
+
+
+ /* get list of all interfaces */
+ size = sizeof(struct ifreq);
+ ifc.ifc_buf = NULL;
+ ifc.ifc_len = size;
+
+ while (ifc.ifc_len == size) {
+ size *= 2;
+
+ free(ifc.ifc_buf);
+ ifc.ifc_len = size;
+ ifc.ifc_buf = malloc(size);
+ if (ioctl(rpc_get_fd(rpc), SIOCGIFCONF, (caddr_t)&ifc) < 0) {
+ rpc_destroy_context(rpc);
+ free(ifc.ifc_buf);
+ return NULL;
+ }
+ }
+
+ for (i = 0; (unsigned)i < ifc.ifc_len / sizeof(struct ifconf); i++) {
+ char bcdd[16];
+
+ if (ifc.ifc_req[i].ifr_addr.sa_family != AF_INET) {
+ continue;
+ }
+ if (ioctl(rpc_get_fd(rpc), SIOCGIFFLAGS, &ifc.ifc_req[i]) < 0) {
+ rpc_destroy_context(rpc);
+ free(ifc.ifc_buf);
+ return NULL;
+ }
+ if (!(ifc.ifc_req[i].ifr_flags & IFF_UP)) {
+ continue;
+ }
+ if (ifc.ifc_req[i].ifr_flags & IFF_LOOPBACK) {
+ continue;
+ }
+ if (!(ifc.ifc_req[i].ifr_flags & IFF_BROADCAST)) {
+ continue;
+ }
+ if (ioctl(rpc_get_fd(rpc), SIOCGIFBRDADDR, &ifc.ifc_req[i]) < 0) {
+ rpc_destroy_context(rpc);
+ free(ifc.ifc_buf);
+ return NULL;
+ }
+ if (getnameinfo(&ifc.ifc_req[i].ifr_broadaddr, sizeof(struct sockaddr_in), &bcdd[0], sizeof(bcdd), NULL, 0, NI_NUMERICHOST) < 0) {
+ rpc_destroy_context(rpc);
+ free(ifc.ifc_buf);
+ return NULL;
+ }
+ if (rpc_set_udp_destination(rpc, bcdd, 111, 1) < 0) {
+ rpc_destroy_context(rpc);
+ free(ifc.ifc_buf);
+ return NULL;
+ }
+
+ if (rpc_pmap_callit_async(rpc, MOUNT_PROGRAM, 2, 0, NULL, 0, callit_cb, &data) < 0) {
+ rpc_destroy_context(rpc);
+ free(ifc.ifc_buf);
+ return NULL;
+ }
+ }
+ free(ifc.ifc_buf);
+
+ gettimeofday(&tv_start, NULL);
+ for(;;) {
+ int mpt;
+
+ pfd.fd = rpc_get_fd(rpc);
+ pfd.events = rpc_which_events(rpc);
+
+ gettimeofday(&tv_current, NULL);
+ mpt = 1000
+ - (tv_current.tv_sec *1000 + tv_current.tv_usec / 1000)
+ + (tv_start.tv_sec *1000 + tv_start.tv_usec / 1000);
+
+ if (poll(&pfd, 1, mpt) < 0) {
+ free_nfs_srvr_list(data.srvrs);
+ rpc_destroy_context(rpc);
+ return NULL;
+ }
+ if (pfd.revents == 0) {
+ break;
+ }
+
+ if (rpc_service(rpc, pfd.revents) < 0) {
+ break;
+ }
+ }
+
+ rpc_destroy_context(rpc);
+
+ if (data.status != 0) {
+ free_nfs_srvr_list(data.srvrs);
+ return NULL;
+ }
+
+ return data.srvrs;
+}
data->nfsfh->offset = data->max_offset;
data->cb(data->max_offset - data->start_offset, nfs, data->buffer, data->private_data);
+
free_nfs_cb_data(data);
free(mdata);
}
* reads and collect into a reassembly buffer.
* we send all reads in parallell so that performance is still good.
*/
+ data->max_offset = offset;
data->start_offset = offset;
data->buffer = malloc(count);
#include <stdio.h>
#include <strings.h>
#include <stdlib.h>
+#include <errno.h>
#include <rpc/rpc.h>
#include <rpc/xdr.h>
#include <rpc/rpc_msg.h>
pdu->xdr_decode_bufsize = xdr_decode_bufsize;
xdrmem_create(&pdu->xdr, rpc->encodebuf, rpc->encodebuflen, XDR_ENCODE);
- xdr_setpos(&pdu->xdr, 4); /* skip past the record marker */
+ if (rpc->is_udp == 0) {
+ xdr_setpos(&pdu->xdr, 4); /* skip past the record marker */
+ }
bzero(&msg, sizeof(struct rpc_msg));
msg.rm_xid = pdu->xid;
size = xdr_getpos(&pdu->xdr);
+ /* for udp we dont queue, we just send it straight away */
+ if (rpc->is_udp != 0) {
+ if (sendto(rpc->fd, rpc->encodebuf, size, MSG_DONTWAIT, rpc->udp_dest, sizeof(struct sockaddr_in)) < 0) {
+ rpc_set_error(rpc, "Sendto failed with errno %s", strerror(errno));
+ rpc_free_pdu(rpc, pdu);
+ return -1;
+ }
+ SLIST_ADD_END(&rpc->waitpdu, pdu);
+ return 0;
+ }
+
/* write recordmarker */
xdr_setpos(&pdu->xdr, 0);
recordmarker = (size - 4) | 0x80000000;
bzero(&xdr, sizeof(XDR));
xdrmem_create(&xdr, buf, size, XDR_DECODE);
- if (xdr_int(&xdr, &recordmarker) == 0) {
- rpc_set_error(rpc, "xdr_int reading recordmarker failed");
- xdr_destroy(&xdr);
- return -1;
+ if (rpc->is_udp == 0) {
+ if (xdr_int(&xdr, &recordmarker) == 0) {
+ rpc_set_error(rpc, "xdr_int reading recordmarker failed");
+ xdr_destroy(&xdr);
+ return -1;
+ }
}
pos = xdr_getpos(&xdr);
if (xdr_int(&xdr, (int *)&xid) == 0) {
if (pdu->xid != xid) {
continue;
}
- SLIST_REMOVE(&rpc->waitpdu, pdu);
+ if (rpc->is_udp == 0 || rpc->is_broadcast == 0) {
+ SLIST_REMOVE(&rpc->waitpdu, pdu);
+ }
if (rpc_process_reply(rpc, pdu, &xdr) != 0) {
rpc_set_error(rpc, "rpc_procdess_reply failed");
}
xdr_destroy(&xdr);
- rpc_free_pdu(rpc, pdu);
+ if (rpc->is_udp == 0 || rpc->is_broadcast == 0) {
+ rpc_free_pdu(rpc, pdu);
+ }
return 0;
}
rpc_set_error(rpc, "No matching pdu found for xid:%d", xid);
#include <sys/filio.h>
#endif
#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
#include "libnfs.h"
#include "libnfs-raw.h"
#include "libnfs-private.h"
{
int events = rpc->is_connected ? POLLIN : POLLOUT;
+ if (rpc->is_udp != 0) {
+ /* for udp sockets we only wait for pollin */
+ return POLLIN;
+ }
+
if (rpc->outqueue) {
events |= POLLOUT;
}
return -1;
}
+ if (rpc->is_udp) {
+ char *buf;
+ socklen_t socklen = sizeof(rpc->udp_src);
+
+ buf = malloc(available);
+ if (buf == NULL) {
+ rpc_set_error(rpc, "Failed to malloc buffer for recvfrom");
+ return -1;
+ }
+ count = recvfrom(rpc->fd, buf, available, MSG_DONTWAIT, (struct sockaddr *)&rpc->udp_src, &socklen);
+ if (count < 0) {
+ rpc_set_error(rpc, "Failed recvfrom: %s", strerror(errno));
+ free(buf);
+ }
+ if (rpc_process_pdu(rpc, buf, count) != 0) {
+ rpc_set_error(rpc, "Invalid/garbage pdu received from server. Ignoring PDU");
+ free(buf);
+ return -1;
+ }
+ free(buf);
+ return 0;
+ }
+
/* read record marker, 4 bytes at the beginning of every pdu */
if (rpc->inbuf == NULL) {
rpc->insize = 4;
return -1;
}
+ if (rpc->is_udp != 0) {
+ rpc_set_error(rpc, "Trying to connect on UDP socket");
+ return -1;
+ }
+
sin->sin_family = AF_INET;
sin->sin_port = htons(port);
if (inet_pton(AF_INET, server, &sin->sin_addr) != 1) {
return 0;
}
+
+
+int rpc_bind_udp(struct rpc_context *rpc, char *addr, int port)
+{
+ struct addrinfo *ai = NULL;
+ char service[6];
+
+ if (rpc->is_udp == 0) {
+ rpc_set_error(rpc, "Cant not bind UDP. Not UDP context");
+ return -1;
+ }
+
+ snprintf(service, 6, "%d", port);
+ if (getaddrinfo(addr, service, NULL, &ai) != 0) {
+ rpc_set_error(rpc, "Invalid address:%s. "
+ "Can not resolv into IPv4/v6 structure.");
+ return -1;
+ }
+
+ switch(ai->ai_family) {
+ case AF_INET:
+ rpc->fd = socket(ai->ai_family, SOCK_DGRAM, 0);
+ if (rpc->fd == -1) {
+ rpc_set_error(rpc, "Failed to create UDP socket: %s", strerror(errno));
+ freeaddrinfo(ai);
+ return -1;
+ }
+
+ if (bind(rpc->fd, (struct sockaddr *)ai->ai_addr, sizeof(struct sockaddr_in)) != 0) {
+ rpc_set_error(rpc, "Failed to bind to UDP socket: %s",strerror(errno));
+ freeaddrinfo(ai);
+ return -1;
+ }
+ break;
+ default:
+ rpc_set_error(rpc, "Can not handle UPD sockets of family %d yet", ai->ai_family);
+ freeaddrinfo(ai);
+ return -1;
+ }
+
+ freeaddrinfo(ai);
+
+ return 0;
+}
+
+int rpc_set_udp_destination(struct rpc_context *rpc, char *addr, int port, int is_broadcast)
+{
+ struct addrinfo *ai = NULL;
+ char service[6];
+
+ if (rpc->is_udp == 0) {
+ rpc_set_error(rpc, "Can not set destination sockaddr. Not UDP context");
+ return -1;
+ }
+
+ snprintf(service, 6, "%d", port);
+ if (getaddrinfo(addr, service, NULL, &ai) != 0) {
+ rpc_set_error(rpc, "Invalid address:%s. "
+ "Can not resolv into IPv4/v6 structure.");
+ return -1;
+ }
+
+ if (rpc->udp_dest) {
+ free(rpc->udp_dest);
+ rpc->udp_dest = NULL;
+ }
+ rpc->udp_dest = malloc(ai->ai_addrlen);
+ if (rpc->udp_dest == NULL) {
+ rpc_set_error(rpc, "Out of memory. Failed to allocate sockaddr structure");
+ return -1;
+ }
+ memcpy(rpc->udp_dest, ai->ai_addr, ai->ai_addrlen);
+ freeaddrinfo(ai);
+
+ rpc->is_broadcast = is_broadcast;
+ setsockopt(rpc->fd, SOL_SOCKET, SO_BROADCAST, &is_broadcast, sizeof(is_broadcast));
+
+ return 0;
+}
+
+struct sockaddr *rpc_get_recv_sockaddr(struct rpc_context *rpc)
+{
+ return (struct sockaddr *)&rpc->udp_src;
+}
int rpc_pmap_getport_async(struct rpc_context *rpc, int program, int version, rpc_cb cb, void *private_data)
{
struct rpc_pdu *pdu;
- struct mapping m;
+ struct pmap_mapping m;
pdu = rpc_allocate_pdu(rpc, PMAP_PROGRAM, PMAP_V2, PMAP_GETPORT, cb, private_data, (xdrproc_t)xdr_int, sizeof(uint32_t));
if (pdu == NULL) {
m.vers = version;
m.prot = IPPROTO_TCP;
m.port = 0;
- if (xdr_mapping(&pdu->xdr, &m) == 0) {
+ if (xdr_pmap_mapping(&pdu->xdr, &m) == 0) {
rpc_set_error(rpc, "XDR error: Failed to encode data for portmap/getport call");
rpc_free_pdu(rpc, pdu);
return -1;
return 0;
}
+
+int rpc_pmap_callit_async(struct rpc_context *rpc, int program, int version, int procedure, const char *data, int datalen, rpc_cb cb, void *private_data)
+{
+ struct rpc_pdu *pdu;
+ struct pmap_call_args ca;
+
+ pdu = rpc_allocate_pdu(rpc, PMAP_PROGRAM, PMAP_V2, PMAP_CALLIT, cb, private_data, (xdrproc_t)xdr_pmap_call_result, sizeof(pmap_call_result));
+ if (pdu == NULL) {
+ rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for portmap/callit call");
+ return -1;
+ }
+
+ ca.prog = program;
+ ca.vers = version;
+ ca.proc = procedure;
+ ca.args.args_len = datalen;
+ ca.args.args_val = data;
+
+ if (xdr_pmap_call_args(&pdu->xdr, &ca) == 0) {
+ rpc_set_error(rpc, "XDR error: Failed to encode data for portmap/callit call");
+ rpc_free_pdu(rpc, pdu);
+ return -1;
+ }
+
+ if (rpc_queue_pdu(rpc, pdu) != 0) {
+ rpc_set_error(rpc, "Failed to queue portmap/callit pdu: %s", rpc_get_error(rpc));
+ return -1;
+ }
+
+ return 0;
+}
const PMAP_PORT = 111; /* portmapper port number */
-struct mapping {
+struct pmap_mapping {
unsigned int prog;
unsigned int vers;
unsigned int prot;
unsigned int port;
};
-struct call_args {
+struct pmap_call_args {
unsigned int prog;
unsigned int vers;
unsigned int proc;
opaque args<>;
};
+struct pmap_call_result {
+ unsigned int port;
+ opaque res<>;
+};
program PMAP_PROGRAM {
version PMAP_V2 {
void
- PMAP_NULL(void) = 0;
+ PMAP_NULL(void) = 0;
bool
- PMAP_SET(mapping) = 1;
+ PMAP_SET(pmap_mapping) = 1;
bool
- PMAP_UNSET(mapping) = 2;
+ PMAP_UNSET(pmap_mapping) = 2;
unsigned int
- PMAP_GETPORT(mapping) = 3;
+ PMAP_GETPORT(pmap_mapping) = 3;
+
+ pmap_call_result
+ PMAP_CALLIT(pmap_call_args) = 5;
} = 2;
} = 100000;