LINUX.ORG.RU

accept(), получение адреса

 , ,


0

1

Пилю значит я очередную свою поделку и встречаю нипонятную вещь: accept() возвращает EFAULT всегда, когда я пытаюсь ему передать что-то кроме нуля вторым и 3-м агрументом. Причем работает он так же, как bind(), но бинд работает, а accept() нет. Насколько я понял bind - это копирование userspace->kernelspace, а accept() kernelspace->userspace в этом может быть причина и я где-то зафейлил. Может надо его как-то поособому юзать и т.п.?

Прикрепляю портынку говнокода своего, прошу сильно не бить и помочь:

#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/epoll.h>
#include <sys/errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <fcntl.h>

int32_t global_eplfd,
        global_defaultdf;
        
struct sockaddr_in global_addr;
struct epoll_event global_epoll_event;
struct epoll_event global_epoll_events[100500];

inline uint8_t exit_nv(int32_t __status) {
    exit(__status);
    return 123;
}

#define CKFD(arg) do{(arg) == -1 && fprintf(stderr, "error : (%s), errno = %d\n", #arg, errno) && exit_nv(1);}while(0);
#define CKEW(arg) ((arg) != -1 || fprintf(stderr, "error : (%s), errno = %d\n", #arg, errno) && exit_nv(1))

void init(void) {
    CKFD(global_eplfd = epoll_create(100500));
    CKFD(global_defaultdf = socket(AF_INET, SOCK_STREAM, 0));
    
    global_addr.sin_family = AF_INET;
    global_addr.sin_port = htons(1123);
    global_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    
    CKFD(bind(global_defaultdf, (struct sockaddr *)&global_addr, sizeof(global_addr)));   
    CKFD(listen(global_defaultdf, 128));
    global_epoll_event.data.fd = global_defaultdf;
    global_epoll_event.events = EPOLLIN | EPOLLRDHUP;
    CKFD(epoll_ctl(global_eplfd, EPOLL_CTL_ADD, global_defaultdf, &global_epoll_event));  
}
int32_t main(int32_t argc, char **argv) {
    init();
    
    int32_t i, ev_fd;
    
    while(CKEW(i = epoll_wait(global_eplfd, global_epoll_events, 100500, -1))) {
        
        fprintf(stdout, "unsleep, ev = %d\n", i);
        
        while(--i > -1 && ((ev_fd = global_epoll_events[i].data.fd) || (fprintf(stderr, "bad_df\n") && exit_nv(1)))) {
            
            fprintf(stdout, "new ev, fd = %d\n", ev_fd);
            
            if(ev_fd == global_defaultdf) {
//                 CKFD(ev_fd = accept4(global_defaultdf, (struct sockaddr *)&global_addr, sizeof(global_addr), SOCK_NONBLOCK));//fail;
                CKFD(ev_fd = accept4(global_defaultdf, 0, 0, SOCK_NONBLOCK));//not fail;
                global_epoll_event.data.fd = ev_fd;
                CKFD(epoll_ctl(global_eplfd, EPOLL_CTL_ADD, ev_fd, &global_epoll_event));  
            } else {
                if(global_epoll_events[i].events & EPOLLRDHUP) { close(ev_fd); fprintf(stdout, "close, fd = %d\n", ev_fd);} else {
                   uint8_t buf[100500];
                   uint16_t end;
                   CKFD(end = read(ev_fd, buf, 100500))
                   buf[end] = '\0';
                   fprintf(stdout, "read message:\n %s", buf);
                }
            }
        }
    }
    return 0;
}



Последнее исправление: o2n3e (всего исправлений: 1)

А теперь прочитай man accept, умник! Нужно передавать указатель на socklen_t, в которой лежит максимально допустимая длина буфера. Блядь, да если ты научишься пользоваться -Wall при конпеляции, 99% подобных идиотских вопросов отпадут сами собой.

anonymous
()
Ответ на: комментарий от anonymous

анонимус прав,
вместо sizeof(global_addr) надо передавать указатель на переменную, значение которой будет sizeof(global_addr)

Harald ★★★★★
()
Ответ на: комментарий от anonymous

Дерзкий

Ты чё такой дерзкий? До чего наглые анонимусы пошли - даже сказать ровно не могут, только недопсевдокопипаста с мануала, которая ниочем не говорит. Что такое «максимально допустимая длина буфера».

Слейся - http://liveworkspace.org/code/5d67a97c3aa0ac75a6bec5905b5d34b1 , учитель хренов.

o2n3e
() автор топика
Ответ на: комментарий от Harald

Спасибо.

Я даже не ожидал, что ему нужно менять аргумент.

o2n3e
() автор топика
Ответ на: комментарий от anonymous

Ещё кое что.

Жаль нельзя редактировать сообщение. Я всегда думал, что в такой реализации сайзоф структуры, которую вернуло ведро можно узнать по первому полю, ядро то жо может узнать сайзоф структуры по первому полю.

Я посмотрел сорцы ведра - и я оказался прав. Этот аргумент абсолютно не нужен и не несёт никакой смысловой нагрузки.

int inet_getname(struct socket *sock, struct sockaddr *uaddr,
			int *uaddr_len, int peer)
{
	struct sock *sk		= sock->sk;
	struct inet_sock *inet	= inet_sk(sk);
	DECLARE_SOCKADDR(struct sockaddr_in *, sin, uaddr);

	sin->sin_family = AF_INET;
	if (peer) {
		if (!inet->inet_dport ||
		    (((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_SYN_SENT)) &&
		     peer == 1))
			return -ENOTCONN;
		sin->sin_port = inet->inet_dport;
		sin->sin_addr.s_addr = inet->inet_daddr;
	} else {
		__be32 addr = inet->inet_rcv_saddr;
		if (!addr)
			addr = inet->inet_saddr;
		sin->sin_port = inet->inet_sport;
		sin->sin_addr.s_addr = addr;
	}
	memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
	*uaddr_len = sizeof(*sin);
	return 0;
}
static int move_addr_to_user(struct sockaddr_storage *kaddr, int klen,
			     void __user *uaddr, int __user *ulen)
{
	int err;
	int len;

	err = get_user(len, ulen);
	if (err)
		return err;
	if (len > klen)
		len = klen;
	if (len < 0 || len > sizeof(struct sockaddr_storage))
		return -EINVAL;
	if (len) {
		if (audit_sockaddr(klen, kaddr))
			return -ENOMEM;
		if (copy_to_user(uaddr, kaddr, len))
			return -EFAULT;
	}
	/*
	 *      "fromlen shall refer to the value before truncation.."
	 *                      1003.1g
	 */
	return __put_user(klen, ulen);
}

Т.е. ну нахрен никак он не сможет вернуть длиннее структуры, которая определна для текущего протокола, а accept() копирует family.

Значит, как и предпологал - этот аргумент не нужен - мой вопрос не «идиотский». Мой вопрос вызван моим понимаем - «не логично - не может быть», но оно есть - это моя не внимательность.

o2n3e
() автор топика
Ответ на: Ещё кое что. от o2n3e

Я посмотрел сорцы ведра - и я оказался прав.

А мне вот совсем иначе видится. Ведь ты не моргнув глазом передаёшь указатель на sockaddr_in (вместо sockaddr_storage, но кого это волнует?), а что если ядро захочет показать тебе более длинный sockaddr_in6? Ну и EFAULT ты получаешь из-за невозможности записать число по тому адресу, который ты передаёшь.

const86 ★★★★★
()
Ответ на: комментарий от const86

Только вот.

Только вот accept() на сокет ipv4 не может вернуть структуру от ipv6. Читай inet_getname(). Даже если это так, а это не так - это не делает 3-й аргумент accept() нужным, ибо он продолжает быть ненужным.

Так же, если я буду юзать сокет ipv6 или другого протокола, либо комбинацию их(если это возможно), то я буду передавать структуру для ipv6 или другого протокола - всё логично.

o2n3e
() автор топика

Это круто чувак!

А вот и нетленка... (Сохранил и заверил нотариально:)) Самоуверенный и дерзкий в других темах, ТС самоуничижается и просит сильно не пинать и помочь разобраться в его говнокоде :)


 while(--i > -1 && ((ev_fd = global_epoll_events[i].data.fd) || (fprintf(stderr, "bad_df\n") && exit_nv(1)))) {

[help mode] А лесенкой не пробовал? Ты попробуй после разбана-то... [/help mode]

slackwarrior ★★★★★
()
Последнее исправление: slackwarrior (всего исправлений: 2)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.